Skip to content

Commit

Permalink
i18n: Add full i18n support
Browse files Browse the repository at this point in the history
This adds i18n support for the app, being
rendered in client-side so that translations
can be loaded dynamically. Implemented language
fallbacks for non-existing translations, plurals
and dynamic placeholders for strings.

Fixes #38
  • Loading branch information
mooncos committed Dec 15, 2017
1 parent 206a75c commit 8659cc6
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .coafile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ bears = SpaceConsistencyBear
default_actions = *: ApplyPatchAction

[all.linelength]
ignore += **.html
ignore += **.html, static/js/i18n/**.json
bears = LineLengthBear

[all.links]
Expand Down
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ env:
node: true
browser: true
es6: true
jquery: true
parserOptions:
ecmaVersion: 2017
plugins:
Expand Down
48 changes: 37 additions & 11 deletions static/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,44 @@ function timeDifference(current, previous) {
const elapsed = current - previous

if (elapsed < msPerMinute) {
return Math.round(elapsed / 1000) + ' seconds ago'
return $.i18n('seconds-ago', Math.round(elapsed / 1000))
} else if (elapsed < msPerHour) {
return Math.round(elapsed / msPerMinute) + ' minutes ago'
return $.i18n('minutes-ago', Math.round(elapsed / msPerMinute))
} else if (elapsed < msPerDay) {
return Math.round(elapsed / msPerHour) + ' hours ago'
return $.i18n('hours-ago', Math.round(elapsed / msPerHour))
} else if (elapsed < msPerMonth) {
return 'approximately ' + Math.round(elapsed / msPerDay) + ' days ago'
return $.i18n('days-ago', Math.round(elapsed / msPerDay))
} else if (elapsed < msPerYear) {
return 'approximately ' + Math.round(elapsed / msPerMonth) + ' months ago'
return $.i18n('months-ago', Math.round(elapsed / msPerMonth))
} else {
return 'approximately ' + Math.round(elapsed / msPerYear) + ' years ago'
return $.i18n('years-ago', Math.round(elapsed / msPerYear))
}
}

// this is needed for time expression translation.
// will be removed when we find a way
// to correctly strip the "ago" segment of a translation
function timeDifferenceFuture(current, previous) {
const msPerMinute = 60 * 1000
const msPerHour = msPerMinute * 60
const msPerDay = msPerHour * 24
const msPerMonth = msPerDay * 30
const msPerYear = msPerDay * 365

const elapsed = current - previous

if (elapsed < msPerMinute) {
return $.i18n('seconds', Math.round(elapsed / 1000))
} else if (elapsed < msPerHour) {
return $.i18n('minutes', Math.round(elapsed / msPerMinute))
} else if (elapsed < msPerDay) {
return $.i18n('hours', Math.round(elapsed / msPerHour))
} else if (elapsed < msPerMonth) {
return $.i18n('days', Math.round(elapsed / msPerDay))
} else if (elapsed < msPerYear) {
return $.i18n('months', Math.round(elapsed / msPerMonth))
} else {
return $.i18n('years', Math.round(elapsed / msPerYear))
}
}

Expand Down Expand Up @@ -51,10 +78,9 @@ function gciProgress() {
'style',
'width:' + percentagePassed + '%;height: 8px;border-radius: 100px;'
)
return (
Math.round(percentagePassed) +
'% passed, ' +
timeDifference(noClaims, current).slice(0, -4) +
' before task claiming ends'
return $.i18n(
'passed',
Math.round(percentagePassed),
timeDifferenceFuture(noClaims, current)
)
}
14 changes: 14 additions & 0 deletions static/js/global.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var current_locale = window.navigator.language

$.i18n({
locale: current_locale,
})

$.i18n()
.load('./static/js/i18n', current_locale)
.done(function() {
$(window).on('load', function() {
console.log('i18n:' + current_locale + ' locale loaded')
$('body').i18n()
})
})
21 changes: 21 additions & 0 deletions static/js/i18n/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"page-title": "Google Code-in 2017 Current Leaders",
"last-updated": "Last updated",
"order-info": "The leading participants for each organization (and their GitHub accounts, if applicable) are listed randomly.",
"tasks-completed": "Tasks Completed",
"footer-text": "Google Code-in and the Google Code-in logo are trademarks of Google Inc.",
"orgs-no-listed": "Organization(s) with no listed leaders",
"passed": "$1% passed, approximately $2 before task claiming ends",
"seconds-ago": "$1 {{PLURAL:$1|second|seconds}} ago",
"minutes-ago": "$1 {{PLURAL:$1|minute|minutes}} ago",
"hours-ago": "$1 {{PLURAL:$1|hour|hours}} ago",
"days-ago": "$1 {{PLURAL:$1|day|days}} ago",
"months-ago": "$1 {{PLURAL:$1|month|months}} ago",
"years-ago": "$1 {{PLURAL:$1|year|years}} ago",
"seconds": "$1 {{PLURAL:$1|second|seconds}}",
"minutes": "$1 {{PLURAL:$1|minute|minutes}}",
"hours": "$1 {{PLURAL:$1|hour|hours}}",
"days": "$1 {{PLURAL:$1|day|days}}",
"months": "$1 {{PLURAL:$1|month|months}}",
"years": "$1 {{PLURAL:$1|year|years}}"
}
21 changes: 21 additions & 0 deletions static/js/i18n/es.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"page-title": "Líderes actuales de Google Code-in 2017",
"last-updated": "Última actualización",
"order-info": "Los participantes líderes de cada organización (y sus respectivas cuentas de GitHub) están listados de forma aleatoria.",
"tasks-completed": "Tareas completadas",
"footer-text": "Google Code-in y su logotipo son marcas registradas de Google Inc.",
"orgs-no-listed": "Organizacion(es) sin listado de líderes",
"passed": "$1% completado, aproximadamente $2 hasta que termine el periodo de tareas",
"seconds-ago": "hace $1 {{PLURAL:$1|segundo|segundos}}",
"minutes-ago": "hace $1 {{PLURAL:$1|minuto|minutos}}",
"hours-ago": "hace $1 {{PLURAL:$1|hora|horas}}",
"days-ago": "hace $1 {{PLURAL:$1|día|días}}",
"months-ago": "hace $1 {{PLURAL:$1|mes|meses}}",
"years-ago": "hace $1 {{PLURAL:$1|año|años}}",
"seconds": "$1 {{PLURAL:$1|segundo|segundos}}",
"minutes": "$1 {{PLURAL:$1|minuto|minutos}}",
"hours": "$1 {{PLURAL:$1|hora|horas}}",
"days": "$1 {{PLURAL:$1|día|días}}",
"months": "$1 {{PLURAL:$1|mes|meses}}",
"years": "$1 {{PLURAL:$1|año|años}}"
}
37 changes: 26 additions & 11 deletions templates/main.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,28 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="og:title" content="GCI Current Leaders">
<meta property="og:description" content="Google Code-in 2017 Current Leaders">
<meta property="og:image" content="{{{rootURL}}}/images/GCI-logo.jpg">
<link rel="stylesheet" href="css/main.css">
<meta property="og:image" content="{{{rootURL}}}/static/images/GCI-logo.jpg">
<link rel="stylesheet" href="static/css/main.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!-- i18n libraries -->
<script src="https://cdn.rawgit.com/santhoshtr/CLDRPluralRuleParser/8baf9aedc4/src/CLDRPluralRuleParser.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.i18n/1.0.4/jquery.i18n.min.js"></script>
<script src="https://cdn.rawgit.com/wikimedia/jquery.i18n/c932076e/src/jquery.i18n.messagestore.js"></script>
<script src="https://cdn.rawgit.com/wikimedia/jquery.i18n/c932076e/src/jquery.i18n.fallbacks.js"></script>
<script src="https://cdn.rawgit.com/wikimedia/jquery.i18n/c932076e/src/jquery.i18n.language.js"></script>
<script src="https://cdn.rawgit.com/wikimedia/jquery.i18n/c932076e/src/jquery.i18n.parser.js"></script>
<script src="https://cdn.rawgit.com/wikimedia/jquery.i18n/c932076e/src/jquery.i18n.emitter.js"></script>
<!-- global translation engine -->
<script src="static/js/global.js"></script>
</head>
<body>
<div>
<a href="http://codein.withgoogle.com">
<img class="gci-logo" src="images/logos/gci.png" alt="Code-In logo">
</a>
<h1>Google Code-in 2017 Current Leaders</h1>
<h1 data-i18n="page-title">Google Code-in 2017 Current Leaders</h1>
</div>
<small id="time" data-time="{{datetime}}">Last updated: {{datetime}} <span id="ago"></span></small>
<small id="time" data-time="{{datetime}}"><span data-i18n="last-updated">Last updated</span>: {{datetime}} <span id="ago"></span></small>
<div>
<div class="progress-outer">
<div class="progress-inner">
Expand All @@ -27,7 +38,7 @@ <h1>Google Code-in 2017 Current Leaders</h1>
<span class="progress-text"><i id="progress" data-noclaims="{{noClaims}}" data-competitionopen="{{competitionOpen}}"></i></span>
</div>
<hr>
<i>
<i data-i18n="order-info">
The leading participants for each organization (and their GitHub accounts,
if applicable) are listed randomly.
</i>
Expand All @@ -45,7 +56,7 @@ <h3>
{{name}}
</a>
</h3>
<p>Tasks Completed: {{completed_task_instance_count}}</p>
<p><span data-i18n="tasks-completed">Tasks Completed</span>: {{completed_task_instance_count}}</p>
{{#github}}
<a href="https://github.com/{{github}}">
<img
Expand Down Expand Up @@ -110,7 +121,7 @@ <h3>
{{/withLeader}}
</div>
{{#noLeader.length}}
<h3>Organization(s) with no listed leaders</h3>
<h3 data-i18n="orgs-no-listed">Organization(s) with no listed leaders</h3>
<hr>
<div class="orgs">
{{#noLeader}}
Expand All @@ -126,7 +137,7 @@ <h3>
{{name}}
</a>
</h3>
<p>Tasks Completed: {{completed_task_instance_count}}</p>
<p><span data-i18n="tasks-completed">Tasks Completed</span>: {{completed_task_instance_count}}</p>
{{#github}}
<a href="https://github.com/{{github}}">
<img
Expand Down Expand Up @@ -182,9 +193,13 @@ <h3>
</a>
</div>
<footer>
<small>Google Code-in and the Google Code-in logo are trademarks of Google Inc.</small>
<small data-i18n="footer-text">Google Code-in and the Google Code-in logo are trademarks of Google Inc.</small>
</footer>
<script src="js/app.js"></script>
<script src="js/twitter.js"></script>
<script>
$(window).on('load', function() {
$.getScript('static/js/app.js')
})
</script>
<script src="static/js/twitter.js"></script>
</body>
</html>

0 comments on commit 8659cc6

Please sign in to comment.