From 7ef5a49a3e0e69d3b2220e9af74810256ed820d8 Mon Sep 17 00:00:00 2001 From: Freenerd Date: Wed, 10 May 2017 16:29:31 +0200 Subject: [PATCH 01/10] Readme: API changes for selective language support --- Readme.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Readme.md b/Readme.md index e49e8f27f..bec158726 100644 --- a/Readme.md +++ b/Readme.md @@ -17,17 +17,18 @@ OSRM Text Instructions has been translated into [several languages](https://gith ### JavaScript Usage -``` +```js var version = 'v5'; -var language = 'en'; -var options = {}; -var osrmTextInstructions = require('osrm-text-instructions')(version, language, options); +var options = { + languages: [ 'en', 'fr' ] +}; +var osrmTextInstructions = require('osrm-text-instructions')(version, options); -// make your request against the API +// make your request against the API, save result to response variable response.legs.forEach(function(leg) { leg.steps.forEach(function(step) { - instruction = osrmTextInstructions.compile(step) + instruction = osrmTextInstructions.compile(step, 'en') }); }); ``` @@ -35,8 +36,8 @@ response.legs.forEach(function(leg) { parameter | required? | values | description ---|----|----|--- `version` | required | `v5` | Major OSRM version -`language` | required | `en` `de` `zh-Hans` `fr` `nl` `ru` | Language identifier `options.hooks.tokenizedInstruction` | optional | `function(instruction)` | A function to change the raw instruction string before tokens are replaced. Useful to inject custom markup for tokens +`options.languages` | optional | `en` `de` `zh-Hans` `fr` `nl` `ru` | Array of language identifiers that should be supported. Default is loading all language files, which can be huge on websites ### Development #### Architecture From 69aa1105bb832a69823721e3bb58fc60ba54f070 Mon Sep 17 00:00:00 2001 From: Lucie Daeye Date: Mon, 15 May 2017 13:32:21 +0200 Subject: [PATCH 02/10] Add support for multiple language tags --- index.js | 16 +++++++--- languages.js | 71 ++++++++++++++++-------------------------- test/languages_test.js | 41 ++++++++++++------------ 3 files changed, 57 insertions(+), 71 deletions(-) diff --git a/index.js b/index.js index d4f6678cb..b3b26e27d 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,13 @@ -module.exports = function(version, language, options) { +module.exports = function(version, _options) { // load instructions - var instructions = require('./languages').get(language); + var options = {}; + options.hook = {}; + options.hook.tokeninzedInstruction = ((_options || {}).hook || {}).tokeninzedInstruction; + options.languages = _options.languages || ['en', 'fr']; + + // TODO Validate language + + var instructions = require('./languages').get(options.languages); if (Object !== instructions.constructor) throw 'instructions must be object'; if (!instructions[version]) { throw 'invalid version ' + version; } @@ -147,9 +154,8 @@ module.exports = function(version, language, options) { instruction = instructionObject.default; } - var tokenizedInstructionHook = ((options || {}).hooks || {}).tokenizedInstruction; - if (tokenizedInstructionHook) { - instruction = tokenizedInstructionHook(instruction); + if (options.tokenizedInstruction) { + instruction = options.tokenizedInstruction(instruction); } // Replace tokens diff --git a/languages.js b/languages.js index bfe98febf..8b29ad4fc 100644 --- a/languages.js +++ b/languages.js @@ -1,49 +1,30 @@ -// Load all language files excplicitely to allow integration -// with bundling tools like webpack and browserify -var instructionsDe = require('./languages/translations/de.json'); -var instructionsEn = require('./languages/translations/en.json'); -var instructionsEs = require('./languages/translations/es.json'); -var instructionsFr = require('./languages/translations/fr.json'); -var instructionsId = require('./languages/translations/id.json'); -var instructionsNl = require('./languages/translations/nl.json'); -var instructionsRu = require('./languages/translations/ru.json'); -var instructionsSv = require('./languages/translations/sv.json'); -var instructionsVi = require('./languages/translations/vi.json'); -var instructionsZhHans = require('./languages/translations/zh-Hans.json'); - -// Match tag to required language files -var tags = { - 'de': instructionsDe, - 'en': instructionsEn, - 'es': instructionsEs, - 'fr': instructionsFr, - 'id': instructionsId, - 'nl': instructionsNl, - 'ru': instructionsRu, - 'sv': instructionsSv, - 'vi': instructionsVi, - 'zh-Hans': instructionsZhHans -}; - -// A tag can redirect to another tag via the language tag as string value -var redirects = { - 'zh': 'zh-Hans' -}; +// Create a list of supported tags +var supportedTags = [ + 'de', + 'en', + 'es', + 'fr', + 'id', + 'nl', + 'ru', + 'sv', + 'vi', + 'zh-Hans' +]; module.exports = { - tags: tags, - redirects: redirects, - get: function(tag) { - if (this.redirects[tag]) { - // redirect to other tag - this.get(this.redirects[tag]); - } - - var language = this.tags[tag]; - if (!language) { - throw 'invalid language ' + tag; - } + get: function(tags) { + // Loads translation files for only supported and user requested tags + var languages = {}; + tags.forEach(function(tag) { + if (supportedTags.indexOf(tag) === -1) { + throw 'Unsupported language tag: ' + tag; + } else { + languages[tag] = require('./languages/translations/' + tag + '.json'); + } + }); - return language; - } + return languages; + }, + supportedTags: supportedTags }; diff --git a/test/languages_test.js b/test/languages_test.js index 9d4bd7bff..82fd94e0b 100644 --- a/test/languages_test.js +++ b/test/languages_test.js @@ -1,24 +1,13 @@ var tape = require('tape'); -var instructions = require('../index.js'); var languages = require('../languages'); -const tags = Object.keys(languages.tags); - -tape.test('verify language files load', function(assert) { - var step = { - maneuver: { - 'type': 'turn', - 'modifier': 'left' - } - }; - - tags.forEach((t) => { - assert.ok(instructions('v5', t).compile(step), 'has ' + t); - }); - assert.throws( - () => { instructions('v5', 'this-will-never-exist'); }, - 'throws on non-existing language' +tape.test('throws on invalid tags', function(assert) { + assert.throws(function() { + languages.get(['en', 'foo']); + }, + /Unsupported language tag: foo/, + 'throws error when gets foo language tag' ); assert.end(); @@ -27,15 +16,16 @@ tape.test('verify language files load', function(assert) { tape.test('verify language files structure', function(assert) { // check that language files have about the same structure as // the reference english language file - var english = languages.get('en'); + var translations = languages.get(languages.supportedTags); + var english = translations.en; - tags.forEach((l) => { + Object.keys(translations).forEach((l) => { if (l === 'en') return; // do not need to compare to self - var translation = languages.get(l); + var translation = translations[l]; assert.deepEqual( Object.keys(translation.v5), - Object.keys(english.v5), + Object.keys(translations.en.v5), l + ' has correct type keys' ); @@ -56,3 +46,12 @@ tape.test('verify language files structure', function(assert) { assert.end(); }); + +tape.test('verify that instructions are only returned for user requested languages', function(assert) { + var translations = languages.get(['en', 'fr']); + + assert.deepEqual(Object.keys(translations).sort(), ['fr', 'en'].sort(), + 'only returns en and fr'); + + assert.end(); +}); From 4cb013de96149efc11870c732be230c005763099 Mon Sep 17 00:00:00 2001 From: Lucie Daeye Date: Mon, 15 May 2017 17:30:12 +0200 Subject: [PATCH 03/10] Fix tests --- index.js | 71 ++++++++++++++++++++++++------------------- test/fixtures_test.js | 5 +-- test/index_test.js | 47 +++++++++++++++++++--------- 3 files changed, 74 insertions(+), 49 deletions(-) diff --git a/index.js b/index.js index b3b26e27d..ef2478688 100644 --- a/index.js +++ b/index.js @@ -1,47 +1,54 @@ +var languages = require('./languages'); + module.exports = function(version, _options) { // load instructions var options = {}; - options.hook = {}; - options.hook.tokeninzedInstruction = ((_options || {}).hook || {}).tokeninzedInstruction; - options.languages = _options.languages || ['en', 'fr']; + options.hooks = {}; + options.hooks.tokenizedInstruction = ((_options || {}).hooks || {}).tokenizedInstruction; + options.languages = (_options || {}).languages || languages.supportedTags; // TODO Validate language var instructions = require('./languages').get(options.languages); if (Object !== instructions.constructor) throw 'instructions must be object'; - if (!instructions[version]) { throw 'invalid version ' + version; } + Object.keys(instructions).forEach(function(code) { + if (!instructions[code][version]) { throw 'invalid version ' + version; } + }); return { capitalizeFirstLetter: function(string) { return string.charAt(0).toUpperCase() + string.slice(1); }, - ordinalize: function(number) { + ordinalize: function(language, number) { // Transform numbers to their translated ordinalized value - return instructions[version].constants.ordinalize[number.toString()] || ''; + if (!language) throw new Error('No language code provided'); + + return instructions[language][version].constants.ordinalize[number.toString()] || ''; }, - directionFromDegree: function(degree) { + directionFromDegree: function(language, degree) { // Transform degrees to their translated compass direction + if (!language) throw new Error('No language code provided'); if (!degree && degree !== 0) { // step had no bearing_after degree, ignoring return ''; } else if (degree >= 0 && degree <= 20) { - return instructions[version].constants.direction.north; + return instructions[language][version].constants.direction.north; } else if (degree > 20 && degree < 70) { - return instructions[version].constants.direction.northeast; + return instructions[language][version].constants.direction.northeast; } else if (degree >= 70 && degree <= 110) { - return instructions[version].constants.direction.east; + return instructions[language][version].constants.direction.east; } else if (degree > 110 && degree < 160) { - return instructions[version].constants.direction.southeast; + return instructions[language][version].constants.direction.southeast; } else if (degree >= 160 && degree <= 200) { - return instructions[version].constants.direction.south; + return instructions[language][version].constants.direction.south; } else if (degree > 200 && degree < 250) { - return instructions[version].constants.direction.southwest; + return instructions[language][version].constants.direction.southwest; } else if (degree >= 250 && degree <= 290) { - return instructions[version].constants.direction.west; + return instructions[language][version].constants.direction.west; } else if (degree > 290 && degree < 340) { - return instructions[version].constants.direction.northwest; + return instructions[language][version].constants.direction.northwest; } else if (degree >= 340 && degree <= 360) { - return instructions[version].constants.direction.north; + return instructions[language][version].constants.direction.north; } else { throw new Error('Degree ' + degree + ' invalid'); } @@ -66,7 +73,9 @@ module.exports = function(version, _options) { return config.join(''); }, - compile: function(step) { + compile: function(language, step) { + if (!language) throw new Error('No language code provided'); + if (options.languages.indexOf(language) === -1) throw new Error('language code ' + language + ' not loaded'); if (!step.maneuver) throw new Error('No step maneuver provided'); var type = step.maneuver.type; @@ -76,7 +85,7 @@ module.exports = function(version, _options) { if (!type) { throw new Error('Missing step maneuver type'); } if (type !== 'depart' && type !== 'arrive' && !modifier) { throw new Error('Missing step maneuver modifier'); } - if (!instructions[version][type]) { + if (!instructions[language][version][type]) { // Log for debugging console.log('Encountered unknown instruction type: ' + type); // eslint-disable-line no-console // OSRM specification assumes turn types can be added without @@ -87,23 +96,23 @@ module.exports = function(version, _options) { // Use special instructions if available, otherwise `defaultinstruction` var instructionObject; - if (instructions[version].modes[mode]) { - instructionObject = instructions[version].modes[mode]; - } else if (instructions[version][type][modifier]) { - instructionObject = instructions[version][type][modifier]; + if (instructions[language][version].modes[mode]) { + instructionObject = instructions[language][version].modes[mode]; + } else if (instructions[language][version][type][modifier]) { + instructionObject = instructions[language][version][type][modifier]; } else { - instructionObject = instructions[version][type].default; + instructionObject = instructions[language][version][type].default; } // Special case handling var laneInstruction; switch (type) { case 'use lane': - laneInstruction = instructions[version].constants.lanes[this.laneConfig(step)]; + laneInstruction = instructions[language][version].constants.lanes[this.laneConfig(step)]; if (!laneInstruction) { // If the lane combination is not found, default to continue straight - instructionObject = instructions[version]['use lane'].no_lanes; + instructionObject = instructions[language][version]['use lane'].no_lanes; } break; case 'rotary': @@ -154,8 +163,8 @@ module.exports = function(version, _options) { instruction = instructionObject.default; } - if (options.tokenizedInstruction) { - instruction = options.tokenizedInstruction(instruction); + if (options.hooks.tokenizedInstruction) { + instruction = options.hooks.tokenizedInstruction(instruction); } // Replace tokens @@ -164,15 +173,15 @@ module.exports = function(version, _options) { instruction = instruction .replace('{way_name}', wayName) .replace('{destination}', (step.destinations || '').split(',')[0]) - .replace('{exit_number}', this.ordinalize(step.maneuver.exit || 1)) + .replace('{exit_number}', this.ordinalize(language, step.maneuver.exit || 1)) .replace('{rotary_name}', step.rotary_name) .replace('{lane_instruction}', laneInstruction) - .replace('{modifier}', instructions[version].constants.modifier[modifier]) - .replace('{direction}', this.directionFromDegree(step.maneuver.bearing_after)) + .replace('{modifier}', instructions[language][version].constants.modifier[modifier]) + .replace('{direction}', this.directionFromDegree(language, step.maneuver.bearing_after)) .replace('{nth}', nthWaypoint) .replace(/ {2}/g, ' '); // remove excess spaces - if (instructions.meta.capitalizeFirstLetter) { + if (instructions[language].meta.capitalizeFirstLetter) { instruction = this.capitalizeFirstLetter(instruction); } diff --git a/test/fixtures_test.js b/test/fixtures_test.js index 38347bcc6..39e306afa 100644 --- a/test/fixtures_test.js +++ b/test/fixtures_test.js @@ -9,10 +9,7 @@ var constants = require('./constants'); var instructions = require('../index.js'); // Load instructions files for each language -var languages = {}; -Object.keys(require('../languages').tags).forEach((l) => { - languages[l] = instructions('v5', l); -}); +var languages = instructions('v5'); tape.test('verify existance/update fixtures', function(assert) { function clone(obj) { diff --git a/test/index_test.js b/test/index_test.js index 2ec156fc8..65c5ba668 100644 --- a/test/index_test.js +++ b/test/index_test.js @@ -6,10 +6,10 @@ var instructions = require('../index'); var languageInstructions = require('../languages'); tape.test('v5 directionFromDegree', function(assert) { - var v5Instructions = instructions('v5', 'en'); + var v5Instructions = instructions('v5', {languages: ['en']}); assert.equal( - v5Instructions.directionFromDegree(undefined), // eslint-disable-line no-undefined + v5Instructions.directionFromDegree('en', undefined), // eslint-disable-line no-undefined '', 'empty string for undefined' ); @@ -36,14 +36,14 @@ tape.test('v5 directionFromDegree', function(assert) { [ 360, 'north' ] ].forEach((d) => { assert.equal( - v5Instructions.directionFromDegree(d[0]), + v5Instructions.directionFromDegree('en', d[0]), d[1], `${d[0]} degrees is ${d[1]}` ); }); assert.throws( - () => { v5Instructions.directionFromDegree(361); }, + () => { v5Instructions.directionFromDegree('en', 361); }, 'throws on out of bounds degree' ); @@ -51,7 +51,7 @@ tape.test('v5 directionFromDegree', function(assert) { }); tape.test('v5 laneDiagram', function(assert) { - var v5Instructions = instructions('v5', 'en'); + var v5Instructions = instructions('v5'); function makeStep(config) { return { @@ -95,16 +95,39 @@ tape.test('v5 laneDiagram', function(assert) { }); tape.test('v5 compile', function(t) { + t.test('throws an error if no language code provided', function(assert) { + var v5Instructions = instructions('v5'); + + assert.throws(function() { + v5Instructions.compile(); + }, /No language code provided/ + ); + + assert.end(); + }); + + t.test('throws an error if a non supported language code is provided', function(assert) { + var v5Instructions = instructions('v5', {languages: ['en']}); + + assert.throws(function() { + v5Instructions.compile('foo'); + }, /language code foo not loaded/ + ); + + assert.end(); + }); + t.test('respects options.instructionStringHook', function(assert) { - var v5Instructions = instructions('v5', 'en', { + var v5Instructions = instructions('v5', { hooks: { tokenizedInstruction: function(instruction) { return instruction.replace('{way_name}', '{way_name}'); } - } + }, + languages: ['en'] }); - assert.equal(v5Instructions.compile({ + assert.equal(v5Instructions.compile('en', { maneuver: { type: 'turn', modifier: 'left' @@ -116,11 +139,7 @@ tape.test('v5 compile', function(t) { t.test('fixtures match generated instructions', function(assert) { // pre-load instructions - var instructionsPerLanguage = {}; - Object.keys(languageInstructions.tags) - .forEach((t) => { - instructionsPerLanguage[t] = instructions('v5', t); - }); + var instructionsPerLanguage = instructions('v5'); var basePath = path.join(__dirname, 'fixtures', 'v5'); @@ -135,7 +154,7 @@ tape.test('v5 compile', function(t) { Object.keys(fixture.instructions).forEach((l) => { assert.equal( - instructionsPerLanguage[l].compile(fixture.step), + instructionsPerLanguage.compile(l, fixture.step), fixture.instructions[l], `${type}/${file}/${l}` ); From 1a5975fe5af9711f8263c2cfab68c52b662273ad Mon Sep 17 00:00:00 2001 From: Lucie Daeye Date: Tue, 16 May 2017 11:17:14 +0200 Subject: [PATCH 04/10] Change language tags to language code for coherence --- Readme.md | 6 +++--- index.js | 2 +- languages.js | 18 +++++++++--------- scripts/transifex.js | 15 ++++++++------- test/index_test.js | 1 - test/languages_test.js | 8 ++++---- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Readme.md b/Readme.md index bec158726..940d4d4e1 100644 --- a/Readme.md +++ b/Readme.md @@ -70,9 +70,9 @@ To add an own translations: - Go to [Transifex](https://www.transifex.com/project-osrm/osrm-text-instructions/) and create the new translation there - When the translation on Transifex is ready, pull in the translation file: - - Create an empty translation file `echo "{}" > languages/translations/{language_tag}.json` - - Add the new translation file and language tag to `./languages.js` - - If needed: make overrides in `languages/overrides/{language_tag}.json` + - Create an empty translation file `echo "{}" > languages/translations/{language_code}.json` + - Add the new translation file and language code to `./languages.js` + - If needed: make overrides in `languages/overrides/{language_code}.json` - `npm run transifex` - Generate fixture strings for the tests via `UPDATE=1 npm test` (see changes in `git diff`) - Make a PR diff --git a/index.js b/index.js index ef2478688..a77dc2bd8 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,7 @@ module.exports = function(version, _options) { var options = {}; options.hooks = {}; options.hooks.tokenizedInstruction = ((_options || {}).hooks || {}).tokenizedInstruction; - options.languages = (_options || {}).languages || languages.supportedTags; + options.languages = (_options || {}).languages || languages.supportedCodes; // TODO Validate language diff --git a/languages.js b/languages.js index 8b29ad4fc..a62321798 100644 --- a/languages.js +++ b/languages.js @@ -1,5 +1,5 @@ -// Create a list of supported tags -var supportedTags = [ +// Create a list of supported codes +var supportedCodes = [ 'de', 'en', 'es', @@ -13,18 +13,18 @@ var supportedTags = [ ]; module.exports = { - get: function(tags) { - // Loads translation files for only supported and user requested tags + get: function(codes) { + // Loads translation files for only supported and user requested codes var languages = {}; - tags.forEach(function(tag) { - if (supportedTags.indexOf(tag) === -1) { - throw 'Unsupported language tag: ' + tag; + codes.forEach(function(code) { + if (supportedCodes.indexOf(code) === -1) { + throw 'Unsupported language code: ' + code; } else { - languages[tag] = require('./languages/translations/' + tag + '.json'); + languages[code] = require('./languages/translations/' + code + '.json'); } }); return languages; }, - supportedTags: supportedTags + supportedCodes: supportedCodes }; diff --git a/scripts/transifex.js b/scripts/transifex.js index 624bf8efb..6da4e8206 100644 --- a/scripts/transifex.js +++ b/scripts/transifex.js @@ -21,23 +21,24 @@ if (!auth.user || !auth.pass) throw 'invalid transifex.auth'; var urls = {}; urls.api = 'https://www.transifex.com/api/2'; urls.project = 'project/osrm-text-instructions'; -urls.translation = `${urls.api}/${urls.project}/resource/enjson/translation` +urls.translation = `${urls.api}/${urls.project}/resource/enjson/translation`; -Object.keys(languages.tags).forEach((tag) => { - if (tag === 'en') { return }; // no need to download english +Object.keys(languages.codes).forEach((code) => { + // no need to download english + if (code === 'en') { return }; // Download from Transifex - request.get(`${urls.translation}/${tag}`, { auth: auth }, (err, resp, body) => { + request.get(`${urls.translation}/${code}`, {auth: auth}, (err, resp, body) => { if (err) throw err; var content = JSON.parse(JSON.parse(body).content); // Apply language-specific overrides - var override = `${__dirname}/../languages/overrides/${tag}.js` - if(fs.existsSync(override)) { + var override = `${__dirname}/../languages/overrides/${code}.js`; + if (fs.existsSync(override)) { content = require(override)(content); } // Write language file - fs.writeFileSync(`${__dirname}/../languages/translations/${tag}.json`, JSON.stringify(content, null, 4)); + fs.writeFileSync(`${__dirname}/../languages/translations/${code}.json`, JSON.stringify(content, null, 4)); }); }); diff --git a/test/index_test.js b/test/index_test.js index 65c5ba668..69cb29e74 100644 --- a/test/index_test.js +++ b/test/index_test.js @@ -3,7 +3,6 @@ var fs = require('fs'); var tape = require('tape'); var instructions = require('../index'); -var languageInstructions = require('../languages'); tape.test('v5 directionFromDegree', function(assert) { var v5Instructions = instructions('v5', {languages: ['en']}); diff --git a/test/languages_test.js b/test/languages_test.js index 82fd94e0b..d095d41b9 100644 --- a/test/languages_test.js +++ b/test/languages_test.js @@ -2,12 +2,12 @@ var tape = require('tape'); var languages = require('../languages'); -tape.test('throws on invalid tags', function(assert) { +tape.test('throws on invalid codes', function(assert) { assert.throws(function() { languages.get(['en', 'foo']); }, - /Unsupported language tag: foo/, - 'throws error when gets foo language tag' + /Unsupported language code: foo/, + 'throws error when gets foo language code' ); assert.end(); @@ -16,7 +16,7 @@ tape.test('throws on invalid tags', function(assert) { tape.test('verify language files structure', function(assert) { // check that language files have about the same structure as // the reference english language file - var translations = languages.get(languages.supportedTags); + var translations = languages.get(languages.supportedCodes); var english = translations.en; Object.keys(translations).forEach((l) => { From ede84aa437123705e7247689455c1624dcce5239 Mon Sep 17 00:00:00 2001 From: Lucie Daeye Date: Tue, 16 May 2017 11:25:48 +0200 Subject: [PATCH 05/10] fix readme --- Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index 940d4d4e1..0a1935abf 100644 --- a/Readme.md +++ b/Readme.md @@ -27,8 +27,8 @@ var osrmTextInstructions = require('osrm-text-instructions')(version, options); // make your request against the API, save result to response variable response.legs.forEach(function(leg) { - leg.steps.forEach(function(step) { - instruction = osrmTextInstructions.compile(step, 'en') + leg.steps.forEach(function(language, step) { + instruction = osrmTextInstructions.compile(language, step) }); }); ``` From 4cb5671d3e374019c7ce812382d4da18d7cb8ca0 Mon Sep 17 00:00:00 2001 From: Lucie Daeye Date: Tue, 16 May 2017 16:08:47 +0200 Subject: [PATCH 06/10] Fix readme --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 0a1935abf..bf553c534 100644 --- a/Readme.md +++ b/Readme.md @@ -27,7 +27,7 @@ var osrmTextInstructions = require('osrm-text-instructions')(version, options); // make your request against the API, save result to response variable response.legs.forEach(function(leg) { - leg.steps.forEach(function(language, step) { + leg.steps.forEach(function(step) { instruction = osrmTextInstructions.compile(language, step) }); }); From 884e52d641e670921f3adb8cc1003b048a97f621 Mon Sep 17 00:00:00 2001 From: Lucie Daeye Date: Tue, 16 May 2017 16:47:06 +0200 Subject: [PATCH 07/10] linter fix --- package.json | 2 +- scripts/transifex.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 240cc0a01..6c8b20870 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "request": "2.79.0" }, "scripts": { - "lint": "eslint *.js test/*.js", + "lint": "eslint *.js test/*.js scripts/*.js", "pretest": "npm run lint", "test": "tape test/*_test.js", "transifex": "node scripts/transifex.js" diff --git a/scripts/transifex.js b/scripts/transifex.js index 6da4e8206..d3164f584 100644 --- a/scripts/transifex.js +++ b/scripts/transifex.js @@ -25,7 +25,7 @@ urls.translation = `${urls.api}/${urls.project}/resource/enjson/translation`; Object.keys(languages.codes).forEach((code) => { // no need to download english - if (code === 'en') { return }; + if (code === 'en') return; // Download from Transifex request.get(`${urls.translation}/${code}`, {auth: auth}, (err, resp, body) => { From 78c5b457ebf1c9d6fab39b0141d218191115d9fe Mon Sep 17 00:00:00 2001 From: Lucie Daeye Date: Wed, 24 May 2017 09:35:18 +0200 Subject: [PATCH 08/10] Small fixes --- Readme.md | 6 +++--- test/languages_test.js | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Readme.md b/Readme.md index bf553c534..504abf5c5 100644 --- a/Readme.md +++ b/Readme.md @@ -37,7 +37,7 @@ parameter | required? | values | description ---|----|----|--- `version` | required | `v5` | Major OSRM version `options.hooks.tokenizedInstruction` | optional | `function(instruction)` | A function to change the raw instruction string before tokens are replaced. Useful to inject custom markup for tokens -`options.languages` | optional | `en` `de` `zh-Hans` `fr` `nl` `ru` | Array of language identifiers that should be supported. Default is loading all language files, which can be huge on websites +`options.languages` | optional | `en` `de` `zh-Hans` `fr` `nl` `ru` [`etc`](https://github.com/Project-OSRM/osrm-text-instructions/tree/master/languages/translations/) | Array of language identifiers that should be supported. Default is loading all language files, which can be huge on websites ### Development #### Architecture @@ -62,7 +62,7 @@ Fixtures are programatically created and updated via `test/fixtures_test`. To up #### Translations -The main language of this project is English `en`. We support other languages via translations, as seen in `languages/translations`. +The main language of this project is English `en`. We support other languages via translations, as seen in [`languages/translations`](https://github.com/Project-OSRM/osrm-text-instructions/tree/master/languages/translations/). You can help translating on the web via [Transifex](https://www.transifex.com/project-osrm/osrm-text-instructions/) @@ -71,7 +71,7 @@ To add an own translations: - Go to [Transifex](https://www.transifex.com/project-osrm/osrm-text-instructions/) and create the new translation there - When the translation on Transifex is ready, pull in the translation file: - Create an empty translation file `echo "{}" > languages/translations/{language_code}.json` - - Add the new translation file and language code to `./languages.js` + - Add the language code to `./languages.js` - If needed: make overrides in `languages/overrides/{language_code}.json` - `npm run transifex` - Generate fixture strings for the tests via `UPDATE=1 npm test` (see changes in `git diff`) diff --git a/test/languages_test.js b/test/languages_test.js index d095d41b9..fabedb498 100644 --- a/test/languages_test.js +++ b/test/languages_test.js @@ -50,8 +50,11 @@ tape.test('verify language files structure', function(assert) { tape.test('verify that instructions are only returned for user requested languages', function(assert) { var translations = languages.get(['en', 'fr']); - assert.deepEqual(Object.keys(translations).sort(), ['fr', 'en'].sort(), - 'only returns en and fr'); + assert.deepEqual( + Object.keys(translations).sort(), + ['fr', 'en'].sort(), + 'only returns en and fr' + ); assert.end(); }); From f923a065a76aa308266e0b9be6c0d418778db62c Mon Sep 17 00:00:00 2001 From: Lucie Daeye Date: Mon, 29 May 2017 13:04:44 +0200 Subject: [PATCH 09/10] Revert changes: load instructions in all supported languages. --- Readme.md | 8 +++---- index.js | 10 +++----- languages.js | 54 ++++++++++++++++++++++-------------------- test/index_test.js | 7 +++--- test/languages_test.js | 25 +------------------ 5 files changed, 38 insertions(+), 66 deletions(-) diff --git a/Readme.md b/Readme.md index 504abf5c5..c1754a6be 100644 --- a/Readme.md +++ b/Readme.md @@ -19,13 +19,11 @@ OSRM Text Instructions has been translated into [several languages](https://gith ```js var version = 'v5'; -var options = { - languages: [ 'en', 'fr' ] -}; -var osrmTextInstructions = require('osrm-text-instructions')(version, options); +var osrmTextInstructions = require('osrm-text-instructions')(version); // make your request against the API, save result to response variable +var language = 'en'; response.legs.forEach(function(leg) { leg.steps.forEach(function(step) { instruction = osrmTextInstructions.compile(language, step) @@ -71,7 +69,7 @@ To add an own translations: - Go to [Transifex](https://www.transifex.com/project-osrm/osrm-text-instructions/) and create the new translation there - When the translation on Transifex is ready, pull in the translation file: - Create an empty translation file `echo "{}" > languages/translations/{language_code}.json` - - Add the language code to `./languages.js` + - Add the new translation file and language code to `./languages.js` - If needed: make overrides in `languages/overrides/{language_code}.json` - `npm run transifex` - Generate fixture strings for the tests via `UPDATE=1 npm test` (see changes in `git diff`) diff --git a/index.js b/index.js index a77dc2bd8..18c6d85fe 100644 --- a/index.js +++ b/index.js @@ -1,18 +1,14 @@ var languages = require('./languages'); +var instructions = languages.instructions; module.exports = function(version, _options) { // load instructions var options = {}; options.hooks = {}; options.hooks.tokenizedInstruction = ((_options || {}).hooks || {}).tokenizedInstruction; - options.languages = (_options || {}).languages || languages.supportedCodes; - // TODO Validate language - - var instructions = require('./languages').get(options.languages); - if (Object !== instructions.constructor) throw 'instructions must be object'; Object.keys(instructions).forEach(function(code) { - if (!instructions[code][version]) { throw 'invalid version ' + version; } + if (!instructions[code][version]) { throw 'invalid version ' + version + ': ' + code + ' not supported'; } }); return { @@ -75,7 +71,7 @@ module.exports = function(version, _options) { }, compile: function(language, step) { if (!language) throw new Error('No language code provided'); - if (options.languages.indexOf(language) === -1) throw new Error('language code ' + language + ' not loaded'); + if (languages.supportedCodes.indexOf(language) === -1) throw new Error('language code ' + language + ' not loaded'); if (!step.maneuver) throw new Error('No step maneuver provided'); var type = step.maneuver.type; diff --git a/languages.js b/languages.js index a62321798..7853b1c9a 100644 --- a/languages.js +++ b/languages.js @@ -1,30 +1,32 @@ +// Load all language files excplicitely to allow integration +// with bundling tools like webpack and browserify +var instructionsDe = require('./languages/translations/de.json'); +var instructionsEn = require('./languages/translations/en.json'); +var instructionsEs = require('./languages/translations/es.json'); +var instructionsFr = require('./languages/translations/fr.json'); +var instructionsId = require('./languages/translations/id.json'); +var instructionsNl = require('./languages/translations/nl.json'); +var instructionsRu = require('./languages/translations/ru.json'); +var instructionsSv = require('./languages/translations/sv.json'); +var instructionsVi = require('./languages/translations/vi.json'); +var instructionsZhHans = require('./languages/translations/zh-Hans.json'); + + // Create a list of supported codes -var supportedCodes = [ - 'de', - 'en', - 'es', - 'fr', - 'id', - 'nl', - 'ru', - 'sv', - 'vi', - 'zh-Hans' -]; +var instructions = { + 'de': instructionsDe, + 'en': instructionsEn, + 'es': instructionsEs, + 'fr': instructionsFr, + 'id': instructionsId, + 'nl': instructionsNl, + 'ru': instructionsRu, + 'sv': instructionsSv, + 'vi': instructionsVi, + 'zh-Hans': instructionsZhHans +}; module.exports = { - get: function(codes) { - // Loads translation files for only supported and user requested codes - var languages = {}; - codes.forEach(function(code) { - if (supportedCodes.indexOf(code) === -1) { - throw 'Unsupported language code: ' + code; - } else { - languages[code] = require('./languages/translations/' + code + '.json'); - } - }); - - return languages; - }, - supportedCodes: supportedCodes + supportedCodes: Object.keys(instructions), + instructions: instructions }; diff --git a/test/index_test.js b/test/index_test.js index 69cb29e74..56cfb4282 100644 --- a/test/index_test.js +++ b/test/index_test.js @@ -5,7 +5,7 @@ var tape = require('tape'); var instructions = require('../index'); tape.test('v5 directionFromDegree', function(assert) { - var v5Instructions = instructions('v5', {languages: ['en']}); + var v5Instructions = instructions('v5'); assert.equal( v5Instructions.directionFromDegree('en', undefined), // eslint-disable-line no-undefined @@ -106,7 +106,7 @@ tape.test('v5 compile', function(t) { }); t.test('throws an error if a non supported language code is provided', function(assert) { - var v5Instructions = instructions('v5', {languages: ['en']}); + var v5Instructions = instructions('v5'); assert.throws(function() { v5Instructions.compile('foo'); @@ -122,8 +122,7 @@ tape.test('v5 compile', function(t) { tokenizedInstruction: function(instruction) { return instruction.replace('{way_name}', '{way_name}'); } - }, - languages: ['en'] + } }); assert.equal(v5Instructions.compile('en', { diff --git a/test/languages_test.js b/test/languages_test.js index fabedb498..a60327786 100644 --- a/test/languages_test.js +++ b/test/languages_test.js @@ -2,21 +2,10 @@ var tape = require('tape'); var languages = require('../languages'); -tape.test('throws on invalid codes', function(assert) { - assert.throws(function() { - languages.get(['en', 'foo']); - }, - /Unsupported language code: foo/, - 'throws error when gets foo language code' - ); - - assert.end(); -}); - tape.test('verify language files structure', function(assert) { // check that language files have about the same structure as // the reference english language file - var translations = languages.get(languages.supportedCodes); + var translations = languages.instructions; var english = translations.en; Object.keys(translations).forEach((l) => { @@ -46,15 +35,3 @@ tape.test('verify language files structure', function(assert) { assert.end(); }); - -tape.test('verify that instructions are only returned for user requested languages', function(assert) { - var translations = languages.get(['en', 'fr']); - - assert.deepEqual( - Object.keys(translations).sort(), - ['fr', 'en'].sort(), - 'only returns en and fr' - ); - - assert.end(); -}); From b4cdbf58e95132294801d072d2810f29bdba9708 Mon Sep 17 00:00:00 2001 From: Lucie Daeye Date: Tue, 30 May 2017 15:28:45 +0200 Subject: [PATCH 10/10] small fixes --- CHANGELOG.md | 2 ++ Readme.md | 2 +- index.js | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e40673c5..0d44c487e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file. For change - put future changes here +- Breaking API change: move language selection from module loading to `compile(language, step)` + # 0.2.1 2017-04-05 - Add Spanish translation (thanks @josek5494) diff --git a/Readme.md b/Readme.md index c1754a6be..548dd5493 100644 --- a/Readme.md +++ b/Readme.md @@ -35,7 +35,7 @@ parameter | required? | values | description ---|----|----|--- `version` | required | `v5` | Major OSRM version `options.hooks.tokenizedInstruction` | optional | `function(instruction)` | A function to change the raw instruction string before tokens are replaced. Useful to inject custom markup for tokens -`options.languages` | optional | `en` `de` `zh-Hans` `fr` `nl` `ru` [`etc`](https://github.com/Project-OSRM/osrm-text-instructions/tree/master/languages/translations/) | Array of language identifiers that should be supported. Default is loading all language files, which can be huge on websites +`language` | required | `en` `de` `zh-Hans` `fr` `nl` `ru` [and more](https://github.com/Project-OSRM/osrm-text-instructions/tree/master/languages/translations/) | Compiling instructions for the selected language code. ### Development #### Architecture diff --git a/index.js b/index.js index 18c6d85fe..14bc5744a 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,6 @@ var languages = require('./languages'); var instructions = languages.instructions; module.exports = function(version, _options) { - // load instructions var options = {}; options.hooks = {}; options.hooks.tokenizedInstruction = ((_options || {}).hooks || {}).tokenizedInstruction;