diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..07e6e472 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/node_modules diff --git a/index.html b/index.html deleted file mode 100644 index 8540e5f9..00000000 --- a/index.html +++ /dev/null @@ -1,397 +0,0 @@ - - - - Dark Sky Multilingual Text Summaries - - - - - - -

Dark Sky Multilingual Text Summaries

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Dark Sky OutputEnglish
(title
-  very-light-precipitation)
Light Precipitation
(title
-  light-precipitation)
Light Precipitation
(title
-  heavy-precipitation)
Heavy Precipitation
(title
-  heavy-rain)
Heavy Rain
(title
-  medium-sleet)
Sleet
(title
-  heavy-snow)
Heavy Snow
(title
-  medium-wind)
Windy
(title
-  heavy-wind)
Dangerously Windy
(title
-  very-heavy-wind)
Violently Windy
(title
-  low-humidity)
Dry
(title
-  fog)
Foggy
(title
-  (and
-    high-humidity
-    light-clouds))
Humid and Partly Cloudy
(sentence
-  (for-hour
-    clear))
Clear for the hour.
(sentence
-  (starting-in
-    very-light-snow
-    (minutes
-      35)))
Flurries starting in 35 min.
(sentence
-  (stopping-in
-    light-rain
-    (minutes
-      15)))
Light rain stopping in 15 min.
(sentence
-  (clauses
-    (starting-in
-      heavy-sleet
-      (minutes
-        20))
-    (stopping-later
-      (minutes
-        30))))
Heavy sleet starting in 20 min., stopping 30 min. later.
(sentence
-  (clauses
-    (stopping-in
-      medium-rain
-      (minutes
-        25))
-    (starting-again-later
-      (minutes
-        8))))
Rain stopping in 25 min., starting again 8 min. later.
(sentence
-  (for-day
-    medium-clouds))
Mostly cloudy throughout the day.
(sentence
-  (starting
-    very-light-sleet
-    morning))
Light sleet starting in the morning.
(sentence
-  (until
-    medium-wind
-    today-night))
Windy until tonight.
(sentence
-  (during
-    light-wind
-    afternoon))
Breezy in the afternoon.
(sentence
-  (during
-    medium-snow
-    (and
-      later-today-evening
-      tomorrow-morning)))
Snow later this evening and tomorrow morning.
(sentence
-  (clauses
-    (until
-      medium-snow
-      later-today-morning)
-    (starting-again
-      today-evening)))
Snow until later this morning, starting again this evening.
(sentence
-  (clauses
-    (starting
-      heavy-clouds
-      evening)
-    (continuing-until
-      night)))
Overcast starting in the evening, continuing overnight.
(sentence
-  (then
-    (during
-      light-sleet
-      later-today-afternoon)
-    (during
-      medium-snow
-      tomorrow-morning)))
Light sleet later this afternoon, then snow tomorrow morning.
(sentence
-  (then
-    (during
-      light-sleet
-      later-today-afternoon)
-    (during
-      medium-snow
-      tomorrow-morning)))
Light sleet later this afternoon, then snow tomorrow morning.
(sentence
-  (clauses
-    (for-week
-      no-precipitation)
-    (during
-      (peaking
-        temperatures
-        (fahrenheit
-          85))
-      tomorrow)))
No precipitation throughout the week, temperatures peaking at 85°F tomorrow.
(sentence
-  (clauses
-    (over-weekend
-      mixed-precipitation)
-    (during
-      (rising
-        temperatures
-        (celsius
-          32))
-      thursday)))
Mixed precipitation over the weekend, temperatures rising to 32°C on Thursday.
(sentence
-  (clauses
-    (during
-      very-light-rain
-      today)
-    (during
-      (valleying
-        temperatures
-        (fahrenheit
-          15))
-      friday)))
Drizzle today, temperatures bottoming out at 15°F on Friday.
(sentence
-  (clauses
-    (during
-      light-snow
-      (and
-        monday
-        tuesday))
-    (during
-      (falling
-        temperatures
-        (celsius
-          0))
-      sunday)))
Light snow on Monday and Tuesday, temperatures falling to 0°C on Sunday.
(sentence
-  (clauses
-    (during
-      medium-precipitation
-      (range
-        wednesday
-        saturday))
-    (during
-      (peaking
-        temperatures
-        (fahrenheit
-          100))
-      monday)))
Precipitation on Wednesday through Saturday, temperatures peaking at 100°F on Monday.
- - - - - - - diff --git a/index.js b/index.js new file mode 100644 index 00000000..a2f3a634 --- /dev/null +++ b/index.js @@ -0,0 +1,10 @@ +var fs = require("fs"), + path = require("path"), + regex = /^(.+)\.js$/; + +fs.readdirSync(path.join(__dirname, "lib/lang")).forEach(function(pathname) { + var match = regex.exec(pathname); + + if(match) + exports[match[1]] = require("./lib/lang/" + pathname); +}); diff --git a/lib/lang/english.js b/lib/lang/english.js new file mode 100644 index 00000000..336d8ca6 --- /dev/null +++ b/lib/lang/english.js @@ -0,0 +1,111 @@ +module.exports = require("../translate")({ + "clear": "clear", + "no-precipitation": "no precipitation", + "mixed-precipitation": "mixed precipitation", + "very-light-precipitation": "light precipitation", + "light-precipitation": "light precipitation", + "medium-precipitation": "precipitation", + "heavy-precipitation": "heavy precipitation", + "very-light-rain": "drizzle", + "light-rain": "light rain", + "medium-rain": "rain", + "heavy-rain": "heavy rain", + "very-light-sleet": "light sleet", + "light-sleet": "light sleet", + "medium-sleet": "sleet", + "heavy-sleet": "heavy sleet", + "very-light-snow": "flurries", + "light-snow": "light snow", + "medium-snow": "snow", + "heavy-snow": "heavy snow", + "light-wind": "breezy", + "medium-wind": "windy", + "heavy-wind": "dangerously windy", + "very-heavy-wind": "violently windy", + "low-humidity": "dry", + "high-humidity": "humid", + "fog": "foggy", + "light-clouds": "partly cloudy", + "medium-clouds": "mostly cloudy", + "heavy-clouds": "overcast", + "temperatures": "temperatures", + "today-morning": "this morning", + "later-today-morning": "later this morning", + "today-afternoon": "this afternoon", + "later-today-afternoon": "later this afternoon", + "today-evening": "this evening", + "later-today-evening": "later this evening", + "today-night": "tonight", + "later-today-night": "later tonight", + "tomorrow-morning": "tomorrow morning", + "tomorrow-afternoon": "tomorrow afternoon", + "tomorrow-evening": "tomorrow evening", + "tomorrow-night": "tomorrow night", + "morning": "morning", + "afternoon": "afternoon", + "evening": "evening", + "night": "night", + "today": "today", + "tomorrow": "tomorrow", + "sunday": "Sunday", + "monday": "Monday", + "tuesday": "Tuesday", + "wednesday": "Wednesday", + "thursday": "Thursday", + "friday": "Friday", + "saturday": "Saturday", + "minutes": "$1 min.", + "fahrenheit": "$1\u00B0F", + "celsius": "$1\u00B0C", + "and": "$1 and $2", + "then": "$1, then $2", + "range": "$1 through $2", + "clauses": function(one, two) { + return one + + (one.indexOf(",") === -1 && two.indexOf(",") === -1 ? ", " : "; ") + + two; + }, + "during": function(condition, day) { + return condition + + (day === "today" || day === "tomorrow" ? " " : " on ") + + day; + }, + "for-hour": "$1 for the hour", + "for-day": "$1 throughout the day", + "for-week": "$1 throughout the week", + "over-weekend": "$1 over the weekend", + "starting": "$1 starting $2", + "starting-in": "$1 starting in $2", + "stopping-in": "$1 stopping in $2", + "until": "$1 until $2", + "continuing-until": "$1 continuing until $2", + "peaking": "$1 peaking at $2", + "rising": "$1 rising to $2", + "valleying": "$1 bottoming out at $2", + "falling": "$1 falling to $2", + "starting-again": "starting again $1", + "starting-again-later": "starting again $1 later", + "stopping-later": "stopping $1 later", + /* Capitalize the first letter of every word, except if that word is + * "and". (This is a very crude bastardization of proper English titling + * rules, but it is adequate for the purposes of this module.) */ + "title": function(str) { + return str.replace( + /\b(?:a(?!nd\b)|[^\Wa])/g, + function(letter) { + return letter.toUpperCase(); + } + ); + }, + /* Capitalize the first word of the sentence and end with a period. */ + "sentence": function(str) { + /* Capitalize. */ + str = str.charAt(0).toUpperCase() + str.slice(1); + + /* Add a period if there isn't already one. */ + if(str.charAt(str.length - 1) !== ".") + str += "."; + + return str; + } +}); diff --git a/lib/translate.js b/lib/translate.js new file mode 100644 index 00000000..f7a4b718 --- /dev/null +++ b/lib/translate.js @@ -0,0 +1,29 @@ +module.exports = function(template) { + function parse(expr) { + if(typeof expr === "number") + return expr.toString(); + + else if(typeof expr === "string") { + if(template.hasOwnProperty(expr)) + return template[expr]; + } + + else if(Array.isArray(expr)) { + if(template.hasOwnProperty(expr[0])) { + if(typeof template[expr[0]] === "string") + return template[expr[0]].replace(/\$\d+/g, function(n) { + return parse(expr[n.slice(1)|0]); + }); + + if(typeof template[expr[0]] === "function") + return template[expr[0]].apply(null, expr.slice(1).map(function(arg) { + return parse(arg); + })); + } + } + + throw new Error("Invalid expression."); + } + + return parse; +}; diff --git a/src/darksky.js b/src/darksky.js deleted file mode 100644 index d238d108..00000000 --- a/src/darksky.js +++ /dev/null @@ -1,33 +0,0 @@ -var DarkSky; - -(function() { - "use strict"; - - DarkSky = { - translate: function parse(template, expr) { - if(typeof expr === "number") - return expr.toString(); - - else if(typeof expr === "string") { - if(template.hasOwnProperty(expr)) - return template[expr]; - } - - else if(Array.isArray(expr)) { - if(template.hasOwnProperty(expr[0])) { - if(typeof template[expr[0]] === "string") - return template[expr[0]].replace(/\$\d+/g, function(n) { - return parse(template, expr[n.slice(1)|0]); - }); - - if(typeof template[expr[0]] === "function") - return template[expr[0]].apply(null, expr.slice(1).map(function(arg) { - return parse(template, arg); - })); - } - } - - throw new Error("Invalid expression."); - } - }; -})(); diff --git a/src/lang/en.js b/src/lang/en.js deleted file mode 100644 index bf1bc76e..00000000 --- a/src/lang/en.js +++ /dev/null @@ -1,111 +0,0 @@ -var DarkSkyEnglish = { - "clear": "clear", - "no-precipitation": "no precipitation", - "mixed-precipitation": "mixed precipitation", - "very-light-precipitation": "light precipitation", - "light-precipitation": "light precipitation", - "medium-precipitation": "precipitation", - "heavy-precipitation": "heavy precipitation", - "very-light-rain": "drizzle", - "light-rain": "light rain", - "medium-rain": "rain", - "heavy-rain": "heavy rain", - "very-light-sleet": "light sleet", - "light-sleet": "light sleet", - "medium-sleet": "sleet", - "heavy-sleet": "heavy sleet", - "very-light-snow": "flurries", - "light-snow": "light snow", - "medium-snow": "snow", - "heavy-snow": "heavy snow", - "light-wind": "breezy", - "medium-wind": "windy", - "heavy-wind": "dangerously windy", - "very-heavy-wind": "violently windy", - "low-humidity": "dry", - "high-humidity": "humid", - "fog": "foggy", - "light-clouds": "partly cloudy", - "medium-clouds": "mostly cloudy", - "heavy-clouds": "overcast", - "temperatures": "temperatures", - "today-morning": "this morning", - "later-today-morning": "later this morning", - "today-afternoon": "this afternoon", - "later-today-afternoon": "later this afternoon", - "today-evening": "this evening", - "later-today-evening": "later this evening", - "today-night": "tonight", - "later-today-night": "later tonight", - "tomorrow-morning": "tomorrow morning", - "tomorrow-afternoon": "tomorrow afternoon", - "tomorrow-evening": "tomorrow evening", - "tomorrow-night": "tomorrow night", - "morning": "morning", - "afternoon": "afternoon", - "evening": "evening", - "night": "night", - "today": "today", - "tomorrow": "tomorrow", - "sunday": "Sunday", - "monday": "Monday", - "tuesday": "Tuesday", - "wednesday": "Wednesday", - "thursday": "Thursday", - "friday": "Friday", - "saturday": "Saturday", - "minutes": "$1 min.", - "fahrenheit": "$1\u00B0F", - "celsius": "$1\u00B0C", - "and": "$1 and $2", - "then": "$1, then $2", - "range": "$1 through $2", - "clauses": function(one, two) { - return one + - (one.indexOf(",") === -1 && two.indexOf(",") === -1 ? ", " : "; ") + - two; - }, - "during": function(condition, day) { - return condition + - (day === "today" || day === "tomorrow" ? " " : " on ") + - day; - }, - "for-hour": "$1 for the hour", - "for-day": "$1 throughout the day", - "for-week": "$1 throughout the week", - "over-weekend": "$1 over the weekend", - "starting": "$1 starting $2", - "starting-in": "$1 starting in $2", - "stopping-in": "$1 stopping in $2", - "until": "$1 until $2", - "continuing-until": "$1 continuing until $2", - "peaking": "$1 peaking at $2", - "rising": "$1 rising to $2", - "valleying": "$1 bottoming out at $2", - "falling": "$1 falling to $2", - "starting-again": "starting again $1", - "starting-again-later": "starting again $1 later", - "stopping-later": "stopping $1 later", - /* Capitalize the first letter of every word, except if that word is - * "and". (This is a very crude bastardization of proper English titling - * rules, but it is adequate for the purposes of this module.) */ - "title": function(str) { - return str.replace( - /\b(?:a(?!nd\b)|[^\Wa])/g, - function(letter) { - return letter.toUpperCase(); - } - ); - }, - /* Capitalize the first word of the sentence and end with a period. */ - "sentence": function(str) { - /* Capitalize. */ - str = str.charAt(0).toUpperCase() + str.slice(1); - - /* Add a period if there isn't already one. */ - if(str.charAt(str.length - 1) !== ".") - str += "."; - - return str; - } - }; diff --git a/src/sexp.js b/src/sexp.js deleted file mode 100644 index 163e26d4..00000000 --- a/src/sexp.js +++ /dev/null @@ -1,32 +0,0 @@ -var SExp; - -(function() { - "use strict"; - - SExp = { - /* FIXME: This needs a bunch of error-handling. */ - parse: function(str) { - str = str.match(/\(|\)|[^\s\(\)]+/g); - - var stack = [[]], - i, top; - - for(i = 0; i !== str.length; ++i) - if(str[i] === "(") - stack.push([]); - - else if(str[i] === ")") { - top = stack.pop(); - stack[stack.length - 1].push(top); - } - - else if(isNaN(str[i])) - stack[stack.length - 1].push(str[i]); - - else - stack[stack.length - 1].push(+str[i]); - - return stack[0][0]; - } - }; -})(); diff --git a/src/test.js b/src/test.js deleted file mode 100644 index fe172e48..00000000 --- a/src/test.js +++ /dev/null @@ -1,40 +0,0 @@ -var mapping = {"en": DarkSkyEnglish}, - rows = document.getElementById("tests").getElementsByTagName("tr"), - i, cells, input, j, output; - -for(i = 0; i !== rows.length; ++i) { - cells = rows[i].getElementsByTagName("td"); - input = undefined; - - for(j = 0; j !== cells.length; ++j) { - if(cells[j].className === "input") - try { - input = SExp.parse(cells[j].textContent); - } - - catch(e) { - input = undefined; - } - - else if(!input || !mapping.hasOwnProperty(cells[j].className)) - cells[j].className += " skip"; - - else - try { - output = DarkSky.translate(mapping[cells[j].className], input); - - if(output === cells[j].textContent) - cells[j].className += " pass"; - - else { - cells[j].className += " fail"; - cells[j].innerHTML += "
(" + output + ")"; - } - } - - catch(e) { - cells[j].className += " fail"; - cells[j].innerHTML += "
(" + e.message + ")"; - } - } -} diff --git a/test-cases/english.json b/test-cases/english.json new file mode 100644 index 00000000..3f14b8c7 --- /dev/null +++ b/test-cases/english.json @@ -0,0 +1,98 @@ +{ + "Light Precipitation": + ["title", "very-light-precipitation"], + + "Light Precipitation": + ["title", "light-precipitation"], + + "Heavy Precipitation": + ["title", "heavy-precipitation"], + + "Heavy Rain": + ["title", "heavy-rain"], + + "Sleet": + ["title", "medium-sleet"], + + "Heavy Snow": + ["title", "heavy-snow"], + + "Windy": + ["title", "medium-wind"], + + "Dangerously Windy": + ["title", "heavy-wind"], + + "Violently Windy": + ["title", "very-heavy-wind"], + + "Dry": + ["title", "low-humidity"], + + "Foggy": + ["title", "fog"], + + "Humid and Partly Cloudy": + ["title", + ["and", "high-humidity", "light-clouds"]], + + + "Clear for the hour.": + ["sentence", ["for-hour", "clear"]], + + "Flurries starting in 35 min.": + ["sentence", ["starting-in", "very-light-snow", ["minutes", 35]]], + + "Light rain stopping in 15 min.": + ["sentence", ["stopping-in", "light-rain", ["minutes", 15]]], + + "Heavy sleet starting in 20 min., stopping 30 min. later.": + ["sentence", ["clauses", + ["starting-in", "heavy-sleet", ["minutes", 20]], + ["stopping-later", ["minutes", 30]]]], + + "Rain stopping in 25 min., starting again 8 min. later.": + ["sentence", ["clauses", + ["stopping-in", "medium-rain", ["minutes", 25]], + ["starting-again-later", ["minutes", 8]]]], + + + + + "No precipitation throughout the week, temperatures peaking at 85°F tomorrow.": + ["sentence", ["clauses", + ["for-week", "no-precipitation"], + ["during", + ["peaking", "temperatures", ["fahrenheit", 85]], + "tomorrow"]]], + + "Mixed precipitation over the weekend, temperatures rising to 32°C on Thursday.": + ["sentence", ["clauses", + ["over-weekend", "mixed-precipitation"], + ["during", + ["rising", "temperatures", ["celsius", 32]], + "thursday"]]], + + "Drizzle today, temperatures bottoming out at 15°F on Friday.": + ["sentence", ["clauses", + ["during", "very-light-rain", "today"], + ["during", + ["valleying", "temperatures", ["fahrenheit", 15]], + "friday"]]], + + "Light snow on Monday and Tuesday, temperatures falling to 0°C on Sunday.": + ["sentence", ["clauses", + ["during", "light-snow", ["and", "monday", "tuesday"]], + ["during", + ["falling", "temperatures", ["celsius", 0]], + "sunday"]]], + + "Precipitation on Wednesday through Saturday, temperatures peaking at 100°F on Monday.": + ["sentence", ["clauses", + ["during", + "medium-precipitation", + ["range", "wednesday", "saturday"]], + ["during", + ["peaking", "temperatures", ["fahrenheit", 100]], + "monday"]]] +} diff --git a/test.js b/test.js new file mode 100644 index 00000000..e573230f --- /dev/null +++ b/test.js @@ -0,0 +1,29 @@ +var expect = require("chai").expect, + fs = require("fs"), + path = require("path"), + translation = require("./"), + util = require("util"); + +describe("translation", function() { + fs.readdirSync(path.join(__dirname, "test-cases")).forEach(function(lang) { + var name = path.basename(lang, ".json"), + translate = translation[name]; + + describe(name, function() { + var cases = JSON.parse( + fs.readFileSync(path.join(__dirname, "test-cases", lang)) + ); + + Object.keys(cases).forEach(function(summary) { + var source = cases[summary]; + + it( + util.format("should translate %d to \"%s\"", source, summary), + function() { + expect(translate(source)).to.equal(summary); + } + ); + }); + }); + }); +});