Sunday 25 February 2018

Film Review: Silicon Valley Season 2

Time for Season 2 of Silicon Valley!

Season 5 should be out this month, but let's look through Season 2 and see if it's worth watching.



Warning - so crass, so much profanity you wouldn't believe!

This season follows in the footsteps of its predecessor in terms of being utterly vulgar. While it doesn't actually up the ante here, it does follow up strongly in that department.

The Premise

We continue the adventures of the Pied Piper team following their wild success from TechCrunch Disrupt, where they are now being courted by several companies for their breakthrough tech. With the death of Peter Gregory, their future with investment firm Raviga is now in doubt and they have to seek new funding. A lawsuit from their rival and tech giant Hooli spells trouble, only for Russ Hanneman to show up. However, his presence ultimately does more harm than good, and after a whole slew of mishaps, our heroes prevail.

The Characters

The cast has been expanded, though the core remains and do their thing very dependably.

Thomas Middleditch is still the very adorkable and awkward Richard Hendricks. If I had a drink for every time he put his foot in his mouth, the liquor cabinet would be empty. Add to that, his earnestness at the right moments and his commitment to doing the right thing by just about everyone, gets my approval. Now if he could just be less of a pussy.

Also, his habit of being pedantic gets carried over from the previous season and receives a bit of air time. There are also some cute segments where he and Dinesh argue using mathematical principles such as the Reflexive Property, Transitive Property and Additive Inverse Property.

TJ Miller, in the same over-the-top performance as Erlich Bachmann. Crass and arrogant, often hilariously so. Miller doesn't disappoint as he carries on with his extravagant and bombastic portrayal from Season 1.

Zach Woods as Jared Dunn. Sweet and earnest, even as he's unintentionally being sexist towards Carla by trying not to be sexist and possible being too considerate. This is a guy who does comedy even without appearing to try. Also provides a sound platform for the season's solemn moments.
Kumail Nanjiani is the awkward Dinesh Chugtai. He's got his snarky moments, but this season shows us a less likeable side of him - the guy who's so insecure that he'll go to extraordinary lengths to present an image.

Martin Starr, excellent as Bertram Gilfoyle. Same as last time we saw him - cold, deadpan sarcasm and nihilism to the hilt. In this season, he becomes the team's de facto hardware guy.


Chris Diamantopoulos as Russ Hanneman, the brash, self-centered tech businessman. Diamantopoulos, like many of the cast, is obviously enjoying himself hugely hamming it up with that "don't do what you should do, do what you want" schtick. He's entertaining as hell to watch too.

Josh Brener reprises his role as Nelson "Big Head" Bighetti. Awkward, clumsy, and possibly even more of a loser than he was in Season 1. I mean, in Season 1, he at least was shown coding. Producing stuff. Here, he's just a huge goofball. That said, he's certainly a lovable one.

Amanda Crew as Monica Hall. She looks really pretty this time round for some reason, but she's not just a pretty face at all. In fact, she acts as exposition sometimes and provides some very sound advice. And she is the straight man to some of the more outlandish performances in the cast.

Matt Ross as Gavin Belson, the scheming, ruthless CEO of Hooli. Emotional and utterly dramatic at times, this season really hammers home his flair for theatrics and his unparalleled showmanship. The character is such an utter tool, but his talent for persuasion cannot be understated. Whether he's smooth-talking his way through a Board Meeting or inspiring Bighead to further acts of inanity, or suckering some fool into becoming his fall-guy, Belson is the quintessential snake oil salesman.

Ben Feldman plays Ron LaFlamme with aplomb. Has a lot more to do this time as the smarmy lawyer giving Richard legal advice. It never seems to be enough. I love every scene he's in.

Comedian Jimmy O. Yang as Jian Yang. Seems to exist purely as a foil for Erlich, but it's not like TJ Miller doesn't have plenty of material for the character, so what gives? The retarded Chinese man act is wearing a little thin.

Suzanne Cryer as Laurie Bream. At first impression, she comes across as a slightly less competent but just as socially awkward version of Peter Gregory. Pretty funny stuff.

Alice Wetterlund makes her debut as Carla Walton. Deadpan and a gigantic troll. Especially towards Jared, though it's hard to say it's undeserved.

Ian Alda as Marc, Endframe CEO. Smug, cocky... and sloppy, which is what leads to his downfall and ultimately getting that shit-eating grin wiped off his face. A moment I really enjoyed. The actor did a fine job.

Joshua Chang as Seth Lee, the Network Security dude from EndFrame. Anxious and emotional. And sometimes comes across as utterly ungrateful. Most likely character to die of a stroke.

Michael McMillian is Aaron "Double A" Anderson. Weird that he actually kind of looks like a younger version of Peter Gregory!

Dustin Milligan as Blaine and his hot and friendly Asian wife Gina, played by Porter Duong. Blaine comes off as a super-stressed asshole at first, but he subverts that later...
Patrick Fischler as Dr Davis Bannerchek. White-haired with black eyebrows. That straight-man demeanor to Gavin Belson's dramatics is a nice contrast. I like the way the actor looks either awkward or exasperated most of the time.

Matt McCoy appears in only two episodes as Pete Monahan the lawyer... but what a glorious two episodes it is. Utterly serious and deadpan as the recovering alcoholic with a penchant for very uncharacteristic behavior (never displayed onscreen and only alluded to) while high.

Frank Collison as Noah, the cranky old man with a ferrety secret.
Andy Daly as the unnamed doctor. God damn it! This doctor is unhelpful as hell and it is fucking funny to watch!

Bernard White is Denpok, Gavin's spiritual advisor. As per last season, full of mystical bullshit. It's a pity we don't get to see more.

Jill E. Alexander as Patrice. Nothing new here, really. Woefully underused.

Charan Prabhakar as Javeed, now looking down and out from his previously chipper appearance in the Season 1 premier.

Aly Mawji and Brian Tiechnel as the brogrammers at Hooli.They're given a little more to do this time round. and actually get to look stressed. The resentment towards Big Head is palpable. Great job!

And cameos by Drew Houston and the Winklevoss twins, right at the premier!

Also, cameos by the ever-excellent Kara Swisher and Walt Mossberg interviewing Gavin Belson!

The Mood

It goes a little slow at the start, which is perfect. We want some time to properly digest the implications of Pied Piper's success from last season. There are numerous callbacks to last season's events and it even takes a few episodes before the first line of code is written. There's not so much of the frenetic crunch-time vibe going on. And then things get tense as the lawsuit kicks in and kudos to the producers and scriptwriters, they actually keep things funny even as the drama is ratcheted up.

The finale of the season sees the team in tech crisis mode and it is wonderful to watch. The tension and suspense is heightened, and quite a few delightful twists are thrown in.

What I liked



Erlich's t-shirts... again!

Watching Richard stand up for himself at the first investor meeting was great, and it's really funny the way he mangles Erlich's vulgarities. Watching Richard becoming increasingly obnoxious with subsequent investor meeting was still funny, though kind of painful to watch.


"Look a person in the eye and deliver the news with warmth and compassion". All this said in a very clinical tone while studiously avoiding eye contact. The visual irony here was sheer genius.


OK, this is pretty clever. Gilfoyle attempts to sabotage Dinesh by funding his cousin's Kickstarter campaign... and look at his user name. Gilfoyle_Luci4. Luci4! Lucifer! Gilfoyle is a Satanist, remember?

How Erlich obviously wants to impress Russ Hanneman but the dude doesn't give him the time of day. Like, ever. There's something deeper going on here, like maybe Russ finds himself looking in a mirror everytime he looks at Erlich, and doesn't like what he sees.

The part where the gang are discussing the hiring of Carla Walton. Jared thinks it's great to hire her because she's a woman and the company needs female representation. Gilfoyle and Richard disagree - they just want to hire someone qualified, period. And here Dinesh summarizes by adding that it would be better if that someone was a woman, even though the "woman" part of the statement is irrelevant. This is one of the most masterful digs at Silicon Valley's pretentiousness towards diversity. Ever. That burn is classic and probably my favorite the entire season.


This moment, right there, where Richard and Erlich turn the tables on their cranky neighbor Noah and browbeat him into letting Jared stay in his house. Considering that Richard and Erlich spend a healthy amount of time this episode sniping at each other, this is really an awesome bonding moment, portrayed brilliantly by the two actors. Middleditch and Miller play off each other perfectly. Come to think of it, Miller plays off just about anyone else perfectly.


This is one of the nice things about the show. They actually take time out to do real educational stuff amid the comedy. Here, we have Jared explain what a SWOT analysis is.


And the kicker is, Dinesh and Gilfoyle spurn Jared at first, then they do their own SWOT Analysis later on! Albeit for not very professional reasons...


Richard that gigantic nerd (and this applies to Dinesh and Gilfoyle too) goes to confront the guys who tricked him into teaching them his algorithm... and halfway through, he can't help himself, takes up the whiteboard marker and almost starts giving them more pointers. I saw it coming a mile off, but still fucking hilarious.

OK, this quote by Gilfoyle is, to me, the standout of the season.
"My feeling is if you're the CEO of a company and you're dumb enough to leave your login info on a Post-it note on your desk, while the people that you fucking ripped off are physically in your office, it's not a hack. It's barely social engineering. It's more like natural selection."






The creative porn genres at Intersite's porn conference had me in stitches. Who the hell thinks this shit up?!



The scene that Monica, Erlich and Jian Yang delivered about the sentiments against smoking in Silicon Valley. Yet another shot fired at that pretentiously progressive culture. Brilliant!

The courtroom scene, all of it, was riveting. Comedy, drama, the works. Eventually Gavin Belson loses because of his own pettiness.

Gilfoyle calls his server "Anton". Which is pretty neat when you recall that Gilfoyle is a LaVeyan Satanist. Anton LaVey, anyone?

The whole sequence where Richard texts the team to delete everything when he thinks he's going to lose the case, then changing his mind when he wins the case. He predictably runs out of battery juice when he tries to tell the team to stay their hands... but the really nice part, and really sweet, is the way the team keeps procrastinating the act of deleting all of their hard work to the very last moment even without further news from Richard.


And that whole scene where the team (sans Richard) has to fight to stay online while their servers stagger under the load of 300,000 viewers. Fire, smoke, tension, victory... this is great! That scene is pretty iconic!

What I didn't

Hearing Monica relate the story of how Peter Gregory died is fun, but predictable. Felt like a drag.

The entire "Bro" thing was banal and not all that funny. Zack Woods is brilliant here, just not brilliant enough. Still, I guess it fed into important plot points (and not-so-important ones).

The "outed by WiFi" gag was mercifully short, lasting only one episode. All it really did was show me that Dinesh is a pathetic faker and Erlich is a stud. No big deal, and not that interesting. And super awkward.

Russ Hanneman was fun to watch... for most of it. Near the tail end of the season, the act started getting just a little repetitive. It's almost like the producers wanted to put him in every episode, even for a few minutes, just to get their moneys' worth.


The scene where Russ accidentally deletes Intersite's data is dramatic, sure... but somehow seems a bit unbelievable. What, no backup? Come on. Though, watching Gilfoyle sweat is a nice change.


Erlich doing some emergency coding, complete with coding gloves and all. What the fuck? Is that even a thing?

The ending. I get that they wanted a cliffhanger... but really? This was pretty lame compared to everything we watched the whole season.

Conclusion


More of the same. If you watched the first season for over-the-top hijinks, vulgarities and the story of the underdog beating the odds coupled with very real scenarios in the tech industry, Season 2 will not disappoint you. In fact, I'd even venture to say Season 2 has the slight edge over Season 1 now that we actually know what to expect, and I, for one, wasn't disappointed in the least. Season 2 has its flaws, sure. But the good far outweighs the bad.

My Rating

9.5 / 10

By the Transitive Property, this season is awesome!
T___T

Sunday 18 February 2018

To Be A Better Man

We've heard the story of how I went from drunken failure to less-spectacular-failure web developer. There were a few sideplots in between, but they were never elaborated on. But hey, it's Valentine's Day (or thereabouts) and that story needs to be told. Well, not really, but it's something to write about!

As my soul feels the shame
I will grow through this pain
Yes I'm doing all I can
To be a better man

Back then, I was with this girl and we were madly in love. Nights spent snuggling, days spent hand-in-hand, that sort of thing. I'd met her on a drunken weekend, and somehow we spent many more weekends - and weekdays - together. We were young, and, did I mention "in love" yet? There was just one problem - I was a loser. It soon became apparent that I wasn't going to move my career out of the rut it was in, anytime soon, and was just content to spend my time with her, in that haze of sweet romance.

She broke up with me, with a sobering message: Until you can man up, you don't deserve me. We have no future together.

My heart was broken. But I moved on.

Fast forward six years later, I was now an extremely driven web professional and my life revolved around work. I lived, breathed and ate my work. (Not literally. I bet HTML and CSS taste like crap.)

And one day, I found myself needing to clear a lot of accumulated leave, and she happened to contact me. It was nice hearing from her again after so long, and we agreed to spend a few days together in Genting Highlands Resort, just... (ahem) catching up, I guess. When we finally met up again, sparks flew. Many passionate nights followed, and she marveled continually at how much I'd changed from the feckless youth from six years ago. I still smoked like a chimney, but I no longer drank. And I actually had a career plan that was in place.

But as the days went by, a horrifying realization dawned upon me.

Even when we were together, I thought a lot about the work waiting for me back in the office. I obsessed over my pet coding project. I was constantly reviewing, and adjusting, my career plans.

Dead. Buried. Gone.

And this was bad. In the process of becoming the man she always hoped I would become - that ultra-serious, intensely focused and driven professional; I had stopped being that starry-eyed romantic who would not hesitate to jump into a relationship with her. That guy was dead and buried. I'd become the kind of man she could see a future with, but that man only had eyes for his work. There was simply no room for anything else beyond a week-long vacation.

The irony was so thick you could cut it with a knife. It appeared that I'd jumped from one extreme to the other. Funny how life works, eh?

Who got the girl eventually?

Certainly not me. I'm still working my issues out.

But I do hope she finds someone. Someone who can be that man with a plan, and still have room for her.

All my (depleted) love,
T___T

Tuesday 13 February 2018

Web Tutorial: Wet Floor Effect

Hello, and welcome to TeochewThunder's yearly Valentine's Day Special!

Today we're going to replicate a very retro special effect known as WFE - the Wet Floor Effect. That's where an image is reflected against a horizontal plane, as if the plane was a wet floor. Back in the day, we had to Photoshop the hell out of an image in order to achieve this. Now, we have CSS!

And for this tutorial, we will be using this very lovey-dovey image.

vday.jpg

Let's begin with some HTML! Here, we have a div with an id of example.

<!DOCTYPE html>
<html>
    <head>
        <title>Valentine's Day 2018</title>
        <style>

        </style>
    </head>

    <body>
        <div id="example">

        </div>
    </body>
</html>


Style it like so. We use two styling blocks because we'll be reusing some of these properties for other CSS classes soon! The background properties are to ensure that the image aligns nicely. You may or may not do the rounded corners thing, but it helps the WFE show up better. Height and width are specified here, but you may choose to change them based on the image you're using.

<!DOCTYPE html>
<html>
    <head>
        <title>Valentine's Day 2018</title>
        <style>
            #example
            {
                width: 300px;
                height: 200px;
            }

            #example
            {
                background-position: center center;
                background-size: 100% 100%;
                background-repeat: no-repeat;
                border-radius: 15px;
                background-image: url(vday.jpg);
            }
        </style>
    </head>

    <body>
        <div id="example">

        </div>
    </body>
</html>




Before we proceed further, let's do this for better clarity.
            div {outline: 1px solid #FF0000}           

            #example
            {
                width: 300px;
                height: 200px;
            }

            #example
            {
                background-position: center center;
                background-size: 100% 100%;
                background-repeat: no-repeat;
                border-radius: 15px;
                background-image: url(vday.jpg);
            }


Right there!


Now add a div within example, and style it with the CSS class wetfloor.
        <div id="example">
            <div class="wetfloor">

            </div>
        </div>


The wetfloor CSS class should be twice as tall as its container, and just as wide.
            div {outline: 1px solid #FF0000}           

            #example
            {
                width: 300px;
                height: 200px;
            }

            #example
            {
                background-position: center center;
                background-size: 100% 100%;
                background-repeat: no-repeat;
                border-radius: 15px;
                background-image: url(vday.jpg);
            }

            .wetfloor
            {
                width: 100%;
                height: 200%;
            }


You'll see this right here.


Now use a before pseudoselector in wetfloor to cover the first top half of wetfloor, which not-so-concidentally covers the image with a red background. This red background will be removed later; for now I want it there so you can see why the before pseudoselector is important.
            .wetfloor
            {
                width:100%;
                height:200%;
            }

            .wetfloor:before
            {
                display: block;
                content: "";
                width: 100%;
                height: 50%;
                background-color: #FF0000;
            }


Got that?


Now add another div inside the one styled by wetfloor. Style it using the CSS class reflection.
        <div id="example">
            <div class="wetfloor">
                <div class="reflection">
               
                </div>               
            </div>
        </div>


For the CSS class reflection, it will take up the full width of its parent, and only 20% of its height.
            .wetfloor
            {
                width: 100%;
                height: 200%;
            }

            .wetfloor:before
            {
                display: block;
                content: "";
                width: 100%;
                height: 50%;
                background-color: #FF0000;
            }

            .reflection
            {
                width: 100%;
                height: 20%;
            }


See? The before pseudoselector took up the top half of the div styled by wetfloor, so the div styled by reflection can only take up that space beneath the image!


OK, I've made my point. Change this to have no background color.
            .wetfloor:before
            {
                display: block;
                content: "";
                width: 100%;
                height: 50%;
                background-color: none;
            }


Getting there...


Now do this. This is why I wanted this to be in a separate specification block - so that it can be reused by reflection.
            #example, #example .wetfloor .reflection
            {
                background-position: center center;
                background-size: 100% 100%;
                background-repeat: no-repeat;
                border-radius: 15px;
                background-image: url(vday.jpg);
            }


Lovely! We're getting warmer.


Now, of course a reflection on the floor has to be upside down. So modify the reflection CSS class.
            .reflection
            {
                width: 100%;
                height: 20%;
                -webkit-transform: scaleY(-1);
                transform: scaleY(-1);
            }


And now it's upside-down!


And of course, a reflection needs to sort of fade off. This is what we'll do. Add a before pseudoselector to the CSS class reflection. It'll occupy the whole of reflection, as a sort of overlay. And I've even added a gradient (looted from ColorZilla) so that it fades from 100% opacity white from the bottom, to 30% opacity near the top. Why white? Well, because your background is white!
            .reflection
            {
                width: 100%;
                height: 20%;
                -webkit-transform: scaleY(-1);
                transform: scaleY(-1);
            }

            .reflection:before
            {
                display: block;
                content: "";
                width: 100%;
                height: 100%;
                background: -webkit-linear-gradient(bottom, rgba(255,200,200,0.3), rgba(255,200,200,1)); /* For Safari 5.1 to 6.0 */
                    background: -o-linear-gradient(to bottom, rgba(255,200,200,0.3), rgba(255,200,200,1)); /* For Opera 11.1 to 12.0 */
                    background: -moz-linear-gradient(to top, rgba(255,200,200,0.3), rgba(255,200,200,1)); /* For Firefox 3.6 to 15 */
                background: linear-gradient(to bottom, rgba(255,200,200,0.3), rgba(255,200,200,1);
            }


See what I mean?

What if I have a different background?

Well, yeah, then I guess you'll have to change the color specifications. First, let's remove the red outline.
div {outline: 0px solid #FF0000}


Beautiful, right? Let's experiment with a different background color. How about a nice pastel pink?


Let's make these changes.
        <style>
            div {outline: 0px solid #FF0000}           

            body {background-color: #FFCCCC;}

            #example
            {
                width: 300px;
                height: 200px;
            }

            #example, #example .wetfloor .reflection
            {
                background-position: center center;
                background-size: 100% 100%;
                background-repeat: no-repeat;
                border-radius: 15px;
                background-image: url(vday.jpg);
            }

            .wetfloor
            {
                width: 100%;
                height: 200%;
            }

            .wetfloor:before
            {
                display: block;
                content: "";
                width: 100%;
                height: 50%;
                background-color: none;
            }

            .reflection
            {
                width: 100%;
                height: 20%;
                -webkit-transform: scaleY(-1);
                transform: scaleY(-1);
            }

            .reflection:before
            {
                display: block;
                content: "";
                width: 100%;
                height: 100%;
                background: -webkit-linear-gradient(bottom, rgba(255,200,200,0.3), rgba(255,200,200,1)); /* For Safari 5.1 to 6.0 */
                    background: -o-linear-gradient(to bottom, rgba(255,200,200,0.3), rgba(255,200,200,1)); /* For Opera 11.1 to 12.0 */
                    background: -moz-linear-gradient(to top, rgba(255,200,200,0.3), rgba(255,200,200,1)); /* For Firefox 3.6 to 15 */
                background: linear-gradient(to bottom, rgba(255,200,200,0.3), rgba(255,200,200,1);
            }
        </style>


Brilliant!


Note

This won't work in Safari due to a pseudoelement and linear background being used. I'm sure some tinkering will help us get around that, but right now it's Valentine's Day and Chinese New Year in one month and I really can't be arsed.

Wasn't this one hell of a tutorial? I bet you're floored.
T___T

Wednesday 7 February 2018

Web Tutorial: Multilingual CNY Website (Part 3/3)

For the next part, you're gonna want to reverse the changes we made in application_controller.rb.

application_controller.rb
class ApplicationController < ActionController::Base
    protect_from_forgery with: :exception

    before_action :get_langs

    def get_langs
        if cookies[:lang] == nil
        cookies[:lang] =
        {
            :value => "en",
            :expires => 1.year.from_now
        }
        end
    end
end


Now, in the Langs Controller, do this. This is an If statement that checks for the parameter lang's value in the languages property of the Lang Model's allowedVals object. Remember the hash only contains "en" and "cn" as keys, so this condition only kicks in if you try the route "langs/index/en" or "langs/index/cn".

langs_controller.rb
class LangsController < ApplicationController
      def index
            if Lang.allowedVals["languages"].include? params[:lang]

            else
   
            end
      end
end


So if there's a value passed in as an argument, either "en" or "cn", a cookie lang is created with the argument as a value. If not, the cookie lang's value is "en" by default. Basically, this means that we use a cookie to track what language the site is currently in.

langs_controller.rb
class LangsController < ApplicationController
      def index
            if Lang.allowedVals["languages"].include? params[:lang]
                  cookies[:lang] =
                  {
                   :value => params[:lang],
                   :expires => 1.year.from_now
                  }
            else
                  cookies[:lang] =
                  {
                   :value => "en",
                   :expires => 1.year.from_now
                  }       
            end
      end
end


Go to routes.rb in the config folder. Add two more lines. This will ensure that there are two more possible routes - "langs/index", and "langs/index" with a language passed in as an argument.

routes.rb
Rails.application.routes.draw do
        get "langs/index" => "langs#index"
        get "langs/index/:lang" => "langs#index"
        get "welcome/" => "welcome#index"
        get "traits/" => "traits#index"
        get "years/" => "years#index"
        get "fortune/" => "fortune#index"

        root "welcome#index"
end


Now try the route "/langs/index/en". What happens? And then try "/langs/index/cn". Does the language change? What if you tried any other argument, such as "langs/index/" or "/langs/index/esp"? Does it change back to English?

Cool. Of course, we're not going to hit that URL manually every time we want to change the language, so let's create a form element for that. In your application View, create a drop-down list. Set it to run the changeLang() function if the value is changed. We'll create that later.

layouts\application.html.erb

<div class="nav right">
        <span class="lang">
            <select name="ddlLang" onchange="changeLang(this.value)">

            </select>
        </span>
        <br />
        <%=link_to Lang.labels["dogyears"][cookies[:lang]], controller: "years" %> |
        <%=link_to Lang.labels["dogtraits"][cookies[:lang]], controller: "traits" %> |
        <%=link_to Lang.labels["dogfortune"][cookies[:lang]], controller: "fortune" %>
</div>


Create a label for it using the properties of the labels object in the Lang Model.

layouts\application.html.erb
<div class="nav right">
        <span class="lang">
            <%=Lang.labels["language"][cookies[:lang]]%>
            <select name="ddlLang" onchange="changeLang(this.value)">

            </select>
        </span>
        <br />
        <%=link_to Lang.labels["dogyears"][cookies[:lang]], controller: "years" %> |
        <%=link_to Lang.labels["dogtraits"][cookies[:lang]], controller: "traits" %> |
        <%=link_to Lang.labels["dogfortune"][cookies[:lang]], controller: "fortune" %>
</div>


Now, we iterate through the allowedVals property of the Lang Model's labels object and set options based on those.

layouts\application.html.erb
<div class="nav right">
        <span class="lang">
            <%=Lang.labels["language"][cookies[:lang]]%>
            <select name="ddlLang" onchange="changeLang(this.value)">
                <% Lang.allowedVals["languages"].each do |key, value|%>
                    <option value="<%= key %>"><%= value %></option>
                <% end %>
            </select>
        </span>
        <br />
        <%=link_to Lang.labels["dogyears"][cookies[:lang]], controller: "years" %> |
        <%=link_to Lang.labels["dogtraits"][cookies[:lang]], controller: "traits" %> |
        <%=link_to Lang.labels["dogfortune"][cookies[:lang]], controller: "fortune" %>
</div>


Here's the drop-down list. But take a look at the screenshots below and see if you can spot the problem. Yep, no matter what language you specify in the URL, the drop-down list always has "English" selected. Let's fix that!










For this, we'll use a Helper. Call it selectedLang(), and pass key (which is either "en" or "cn") into it.

layouts\application.html.erb
            <select name="ddlLang" onchange="changeLang(this.value)">
                <% Lang.allowedVals["languages"].each do |key, value|%>
                    <option value="<%= key %>" <%= selectedLang(key) %>><%= value %></option>
                <% end %>
            </select>


Open up the application_helper.rb file in the helpers folder. In it, define selectedLang() as a function which accepts the parameter lang, and returns the string "selected" only if lang is equal to the current value of the lang cookie.

application_helper.rb
module ApplicationHelper
        def selectedLang(lang)
                "selected" if lang == cookies[:lang]
        end
end


Now when you specify that the language is "cn", you should see the correct value in the drop-down list!



OK, enough side-tracking...

...back to the changeLang() function, yeah?

Open up the assets folder, then the javascripts folder, and finally the application.js file. Ignore the preachy comment section and create the changeLang() function.

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, or any plugin's
// vendor/assets/javascripts directory 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. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require rails-ujs
//= require turbolinks
//= require_tree .

function changeLang(lang)
{

}


Here, let's do a bit of AJAX. Set your AJAX block to access the "langs/index" route and pass in lang as an argument. You already know what that route does - it'll set the lang cookie's value based on whatever argument you pass in.

application.js
function changeLang(lang)
{
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {

        }
    };
    xmlhttp.open("GET", "../../langs/index/" + lang, true);
    xmlhttp.send();
}


And once that route is accessed and the code executed, reload the page!

application.js
function changeLang(lang)
{
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            location.reload();
        }
    };
    xmlhttp.open("GET", "../../langs/index/" + lang, true);
    xmlhttp.send();
}


Now, try changing the value of the drop-down list. Does it reload the page and display the language you selected? Groovy, baby.

That's all! Happy Chinese New Year!

Thank you very much for your time. Do enjoy the Year of the Dog.

Sonofabitch, one hell of a tutorial, eh?
T___T

Monday 5 February 2018

Web Tutorial: Multilingual CNY Website (Part 2/3)

Welcome back...

One thing we didn't do in the last part was including a navigation bar. Because typing in the URLs constantly has got to be a gigantic pain in the butt. However, the navigational bar was left till now because it involves a new Model and Controller we'll be creating here.

Without further ado, let's create lang.rb in the models folder and langs_controller.rb in the controllers folder. Also create the view index.html.erb in the langs folder of the views folder, though we can pretty much leave it blank. The lang Model and Controller will be used application-wide, and aren't particular to any page.

lang.rb
class Lang

end


langs_controller.rb
class LangsController < ApplicationController
      def index

      end
end


In the Model, we're going to have different objects. And each object will have hashes as properties. Each hash will contain two key-value pairs - one for the English translation and the other for the Chinese translation.

lang.rb
class Lang
        def self.allowedVals
        {
            "languages" => Hash["en" => "English", "cn" => "中文"]
        }
        end

        def self.header
        {
            "header" => Hash["en" => "Happy New Year", "cn" => "新年快乐"],
        }
        end

        def self.labels
        {
            "language" => Hash["en" => "Language", "cn" => "语言"],
            "dogyears" => Hash["en" => "Dog Years", "cn" => "狗年"],
            "dogtraits" => Hash["en" => "Dog Traits", "cn" => "狗性格特征"],
            "dogfortune" => Hash["en" => "2018 Fortune", "cn" => "2018财富预测"],
            "tt" => Hash["en" => "Teochew Thunder", "cn" => "潮州雷"]
        }
        end
end


Open application_controller.rb in the controllers folder of the app folder. This is the application scope, and in it let's specify that get_langs() is always run before any actions of other controllers are executed.

application_controller.rb
class ApplicationController < ActionController::Base
        protect_from_forgery with: :exception

        before_action :get_langs
end


Then define the get_langs() routine. If there is no cookie named lang...

application_controller.rb
class ApplicationController < ActionController::Base
        protect_from_forgery with: :exception

        before_action :get_langs

        def get_langs
                if cookies[:lang] == nil

                end
        end
end


...create a cookie named lang with a value of "en". This basically means that we use the lang cookie to track what language the site is currently using. If there is no such cookie, the default is English.

application_controller.rb
class ApplicationController < ActionController::Base
        protect_from_forgery with: :exception

        before_action :get_langs

        def get_langs
               if cookies[:lang] == nil
                    cookies[:lang] =
                    {
                    :value => "en",
                    :expires => 1.year.from_now
                    }
                end
        end
end


Now, let's test this by altering the application template. Since we're using the Lang Model which is used sitewide, we'll reference its properties directly without going through its Controller. It's not a terribly beautiful solution, but I can live with it. See how we're using the value of the lang cookie, which is either "en" or "cn", as a key to the properties.

layouts\application.html.erb
<!DOCTYPE html>
<html>
    <head>
        <title><%=Lang.header["header"][cookies[:lang]]%></title>
        <%= csrf_meta_tags %>

        <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
        <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
    </head>

    <body>
        <div class="container">
            <div class="header">
                <div class="headertext left">
                    <%=Lang.header["header"][cookies[:lang]]%>!
                </div>
                <div class="nav right">

                </div>
                <div class="clearfix"></div>
            </div>

            <div class="content">
                <%= yield %>
                <div class="clearfix"></div>
            </div>

            <div class="footer">
                <%=Lang.labels["tt"][cookies[:lang]]%> &copy; 2017
            </div>
        </div>
    </body>
</html>


Here, you'll see the values being displayed on-screen!




Now try this. This comments out the If condition and sets the lang cookie to "cn" no matter what.

application_controller.rb
class ApplicationController < ActionController::Base
        protect_from_forgery with: :exception

        before_action :get_langs

        def get_langs
            #if cookies[:lang] == nil
                   cookies[:lang] =
                   {
                   :value => "cn",
                   :expires => 1.year.from_now
                   }
           #end
        end
end


And now the displayed values have changed! But only the template has changed. The body is still in English. That's because the values in the Welcome Model are only in English. Let's fix that!



Instead of string values, we now have hashes containing "en" and "cn" keys, along with English and Chinese translations respectively.

welcome.rb
class Welcome
        def self.lang_content
        {
            "header" => Hash["en" => "Happy New Year", "cn" => "新年快乐"],
            "p1" => Hash["en" => "2018 is the Year of the Dog!", "cn" => "2018年是狗年!"],
            "p2" => Hash["en" => "Please click through the links above to find out more.", "cn" => "如果想了解更多,请点击上面的链接。"],
            "p3" => Hash["en" => "We wish you a happy and smooth-sailing New Year!", "cn" => "祝你新年快乐,万事如意!"]
        }
        end
end


And then we use the lang cookie's value to reference the appropriate key, in the Welcome Controller.

welcome_controller.rb
class WelcomeController < ApplicationController
      def index
            @header = Welcome.lang_content["header"][cookies[:lang]]
            @p1 = Welcome.lang_content["p1"][cookies[:lang]]
            @p2 = Welcome.lang_content["p2"][cookies[:lang]]
            @p3 = Welcome.lang_content["p3"][cookies[:lang]]
      end
end


Ah, now the body text has changed too. Let's get rid of the "Welcome" text. We no longer need it. It's taking up too much space on the display anyway.



welcome\index.html.erb
<div class="left">
    <p><%=@header %> </p>
    <p><%=@p1 %> </p>
    <p><%=@p2 %> </p>
    <p><%=@p3 %> </p>
</div>

<div class="right">
    <%= link_to image_tag("00.png");%>
</div>


Yep!



Now just repeat everything for the other Models, Controllers and Views. You'll notice that there's basically no change in the Year Model and Controller. As mentioned before, the years are four-digit strings and look the same in whatever language, so we took the lazy way out and just coded them straight in the View.

fortune.rb
class Fortune
        def self.lang_content
        {
            "p1" => Hash["en" => "According to traditional Chinese prediction, they will face several challenges in career and wealth. Although they may get enviable developments with rapid progress, they still need to keep modest all the time. Otherwise, the tense interpersonal relationship will likely do harm to their work and money.", "cn" => "根据中国传统的预测,他们将在职业和财富方面面临几个挑战。 虽然他们可能会得到令人羡慕的发展迅速进步,但仍然需要保持温和。 否则,紧张的人际关系可能会损害他们的工作和金钱。"],
            "p2" => Hash["en" => "Most of them are suitable to become group leaders. Their insistent and active personal traits will help them catch development opportunities. In the Year of the Dog, they are advised to do several investments related to cultural project or thermal power project. They are accustomed to earning profits by using strategy or even extreme methods.", "cn" => "他们大多数都适合成为集团领导人。 他们坚持和积极的个人特质将有助于他们抓住发展机遇。 在狗年中,建议与文化项目或火电项目进行多项投资。 他们习惯于通过使用策略甚至极端的方法赚取利润。"],
            "p3" => Hash["en" => "They should not be too self-willed and extreme in ordinary life. Staying calm down, thinking much and speaking little will bring good effects to their health situation. In addition, it is suggested to avoid some exploration activities such as climbing mountains, drifting and bungee jumping. Villa resort near river and forest is a wonderful place to take a holiday.", "cn" => "他们在日常生活中不应该太自私,极端。 保持冷静下来,思考多少,会对他们的健康状况产生好的效果。 此外,建议避免一些探险活动,如登山,漂流和蹦极跳跃。 靠近河流和森林的别墅度假是度过假期的绝佳去处。"]
        }
        end
end


trait.rb
class Trait
        def self.lang_content
        {
            "candid" => Hash["en" => "candid", "cn" => "坦率"],
            "loyal" => Hash["en" => "loyal", "cn" => "忠诚"],
            "intelligent" => Hash["en" => "intelligent", "cn" => "聪明"],
             "cool-headed" => Hash["en" => "cool-headed", "cn" => "冷静"],
            "persistent" => Hash["en" => "persistent", "cn" => "一贯"],
            "active" => Hash["en" => "active", "cn" => "活跃"],
            "impatient" => Hash["en" => "impatient", "cn" => "急躁"],
            "conservative" => Hash["en" => "conservative", "cn" => "保守"],
            "anxious" => Hash["en" => "anxious", "cn" => "焦急"]
        }
        end
end


year.rb
class Year
        def self.lang_content
        {

        }
        end
end


fortune_controller.rb
class FortuneController < ApplicationController
      def index
          @p1 = Fortune.lang_content["p1"][cookies[:lang]]
          @p2 = Fortune.lang_content["p2"][cookies[:lang]]
          @p3 = Fortune.lang_content["p3"][cookies[:lang]]
      end
end


traits_controller.rb
class TraitsController < ApplicationController
      def index
          @candid = Trait.lang_content["candid"][cookies[:lang]]
          @loyal = Trait.lang_content["loyal"][cookies[:lang]]
          @intelligent = Trait.lang_content["intelligent"][cookies[:lang]]
          @coolheaded = Trait.lang_content["cool-headed"][cookies[:lang]]
          @persistent = Trait.lang_content["persistent"][cookies[:lang]]
          @active = Trait.lang_content["active"][cookies[:lang]]
          @impatient = Trait.lang_content["impatient"][cookies[:lang]]
          @conservative = Trait.lang_content["conservative"][cookies[:lang]]
          @anxious = Trait.lang_content["anxious"][cookies[:lang]]
      end
end


year_controller.rb
class YearController < ApplicationController
      def index

      end
end


fortune\index.html.erb
<div class="left">
    <p><%=@p1 %> </p>
    <p><%=@p2 %> </p>
    <p><%=@p3 %> </p>
</div>

<div class="right">
    <%= link_to image_tag("01.png");%>
</div>


traits\index.html.erb
<div class="left">
    <p><%=@candid %> </p>
    <p><%=@loyal %> </p>
    <p><%=@intelligent %> </p>
    <p><%=@coolheaded %> </p>
    <p><%=@persistent %> </p>
    <p><%=@active %> </p>
    <p><%=@impatient %> </p>
    <p><%=@conservative %> </p>
    <p><%=@anxious %> </p>
</div>

<div class="right">
    <%= link_to image_tag("02.png");%>
</div>


fortune\index.html.erb
<div class="left">
    <h2>1982</h2>
    <h2>1994</h2>
    <h2>2006</h2>
    <h2>2018</h2>
    <h2>2030</h2>
</div>

<div class="right">
    <%= link_to image_tag("03.png");%>
</div>


Here's how all the other pages look with the newly selected language.










We're not done yet...

I promised to add a navigation bar, didn't I? We'll get on that now. Open the application.html.erb file from the layouts folder of the views folder.

We'll use the link_to helper for this. Read more on it here, but long story short, we get the appropriate URL by passing in the display string and a reference to the controller as arguments.

layouts\application.html.erb
                <div class="headertext left">
                    <%=link_to Lang.header["header"][cookies[:lang]], controller: "welcome" %>!
                </div>
                <div class="nav right">
                    <%=link_to Lang.labels["dogyears"][cookies[:lang]], controller: "years" %> |
                    <%=link_to Lang.labels["dogtraits"][cookies[:lang]], controller: "traits" %> |
                    <%=link_to Lang.labels["dogfortune"][cookies[:lang]], controller: "fortune" %>
                </div>


Try the links. They ought to work! Though it's not terribly obvious, the text on the left side is now a link that brings you back to the default page, which is Welcome.


Next

We'll implement a mechanism to switch languages at will. Watch for it!