Skip to content

Commit bc40772

Browse files
committed
Accept callback for formatting tokens
Added a formatToken option to compile, getWayName, and tokenize that gives the client an opportunity to manipulate a road name or other token value after any transformations built into this library but before the value is inserted into the overall instruction string.
1 parent c5a621f commit bc40772

File tree

5 files changed

+92
-12
lines changed

5 files changed

+92
-12
lines changed

.eslintrc.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,12 @@
8282
"max-len": "off",
8383
"max-lines": "off",
8484
"max-nested-callbacks": "error",
85-
"max-params": "error",
85+
"max-params": [
86+
"error",
87+
{
88+
"max": 4
89+
}
90+
],
8691
"max-statements": "off",
8792
"max-statements-per-line": "off",
8893
"multiline-ternary": "off",

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Change Log
22
All notable changes to this project will be documented in this file. For change log formatting, see http://keepachangelog.com/
33

4+
## master
5+
6+
- Added a `formatToken` option to `compile`, `getWayName`, and `tokenize` that allows you to manipulate any token value after any grammar or capitalization rules are applied but before the value is inserted into the instruction. [#170](https://github.com/Project-OSRM/osrm-text-instructions/pull/170)
7+
48
## 0.9.0 2017-10-05
59

610
- Added `getBestMatchingLanguage` for determining the closest available language. Pass a user locale into this method before passing the return value into `compile`. [#168](https://github.com/Project-OSRM/osrm-text-instructions/pull/168)

Readme.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,22 @@ parameter | required? | values | description
5050
---|----|----|---
5151
`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.
5252
`step` | required | [OSRM route step object](https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md#routestep-object) | The RouteStep as it comes out of OSRM
53-
`options` | optional | Object with 2 keys: `legIndex` and `legCount`, both having integer values. Used for giving instructions for arriving at waypoints.
53+
`options` | optional | Object | See [below](#options)
54+
55+
##### Options
56+
57+
key | type | description
58+
---|----|----|---
59+
`legCount` | integer | Number of legs in the route
60+
`legIndex` | integer | Zero-based index of the leg containing the step; together with `legIndex`, this option determines whether an arrival instruction indicates which waypoint the user has arrived at
61+
`formatToken` | function | Function that formats the given token value after grammaticalization and capitalization but before the value is inserted into the instruction string
62+
63+
`formatToken` takes two parameters:
64+
65+
* `token`: A string that indicates the kind of token, such as `way_name` or `direction`
66+
* `value`: A grammatical string for this token, capitalized if the token appears at the beginning of the instruction
67+
68+
and returns a string.
5469

5570
### Development
5671
#### Architecture

index.js

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ module.exports = function(version, _options) {
9898
wayName = this.tokenize(language, phrase, {
9999
name: name,
100100
ref: ref
101-
});
101+
}, options);
102102
} else if (name && ref && wayMotorway && (/\d/).test(ref)) {
103103
wayName = ref;
104104
} else if (!name && ref) {
@@ -204,7 +204,7 @@ module.exports = function(version, _options) {
204204
'nth': nthWaypoint
205205
};
206206

207-
return this.tokenize(language, instruction, replaceTokens);
207+
return this.tokenize(language, instruction, replaceTokens, options);
208208
},
209209
grammarize: function(language, name, grammar) {
210210
if (!language) throw new Error('No language code provided');
@@ -226,22 +226,36 @@ module.exports = function(version, _options) {
226226

227227
return name;
228228
},
229-
tokenize: function(language, instruction, tokens) {
229+
tokenize: function(language, instruction, tokens, options) {
230230
if (!language) throw new Error('No language code provided');
231231
// Keep this function context to use in inline function below (no arrow functions in ES4)
232232
var that = this;
233-
var output = instruction.replace(/\{(\w+):?(\w+)?\}/g, function(token, tag, grammar) {
234-
var name = tokens[tag];
235-
if (typeof name !== 'undefined') {
236-
return that.grammarize(language, name, grammar);
237-
}
233+
var startedWithToken = false;
234+
var output = instruction.replace(/\{(\w+)(?::(\w+))?\}/g, function(token, tag, grammar, offset) {
235+
var value = tokens[tag];
238236

239237
// Return unknown token unchanged
240-
return token;
238+
if (typeof value === 'undefined') {
239+
return token;
240+
}
241+
242+
value = that.grammarize(language, value, grammar);
243+
244+
// If this token appears at the beginning of the instruction, capitalize it.
245+
if (offset === 0 && instructions[language].meta.capitalizeFirstLetter) {
246+
startedWithToken = true;
247+
value = that.capitalizeFirstLetter(language, value);
248+
}
249+
250+
if (options && options.formatToken) {
251+
value = options.formatToken(tag, value);
252+
}
253+
254+
return value;
241255
})
242256
.replace(/ {2}/g, ' '); // remove excess spaces
243257

244-
if (instructions[language].meta.capitalizeFirstLetter) {
258+
if (!startedWithToken && instructions[language].meta.capitalizeFirstLetter) {
245259
return this.capitalizeFirstLetter(language, output);
246260
}
247261

test/index_test.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint max-lines: "off" */
12
var path = require('path');
23
var fs = require('fs');
34
var tape = require('tape');
@@ -34,6 +35,47 @@ tape.test('v5 tokenize', function(assert) {
3435
});
3536
assert.equal(missingSecond, 'Can osrm {second}', 'does not replace tokens which are not provided');
3637

38+
var formatsTokens = v5Compiler.tokenize('en', 'Take me {destination}, {way_name}', {
39+
destination: 'home',
40+
'way_name': 'Country Road'
41+
}, {
42+
formatToken: function (token, value) {
43+
if (token === 'destination') {
44+
return '<prosody rate="slow">' + value + '</prosody>';
45+
}
46+
if (token === 'name' || token === 'way_name' || token === 'rotary_name') {
47+
return value.replace('Road', '<prosody rate="slow">Road</prosody>');
48+
}
49+
50+
return value;
51+
}
52+
});
53+
assert.equal(formatsTokens, 'Take me <prosody rate="slow">home</prosody>, Country <prosody rate="slow">Road</prosody>',
54+
'Formats tokens');
55+
56+
var capitalizesTokens = v5Compiler.tokenize('en', '{modifier} turns are prohibited here', {
57+
modifier: 'left'
58+
}, {
59+
formatToken: function (token, value) {
60+
if (token === 'modifier') {
61+
return '<strong>' + value + '</strong>';
62+
}
63+
64+
return value;
65+
}
66+
});
67+
assert.equal(capitalizesTokens, '<strong>Left</strong> turns are prohibited here',
68+
'Capitalizes tokens before formatting');
69+
70+
var formatsGrammaticalTokens = v5Compiler.tokenize('ru', 'Плавно поверните налево на {way_name:accusative}', {
71+
'way_name': 'Бармалеева улица'
72+
}, {
73+
formatToken: function (token, value) {
74+
return token === 'way_name' ? value.toLocaleUpperCase('ru') : value;
75+
}
76+
});
77+
assert.equal(formatsGrammaticalTokens, 'Плавно поверните налево на БАРМАЛЕЕВУ УЛИЦУ',
78+
'Formats tokens after grammaticalization but before insertion');
3779

3880
assert.end();
3981
});

0 commit comments

Comments
 (0)