string-apostrophes1.3.2
§ Quick Take
import { strict as assert } from "assert";
import {
convertOne,
convertAll,
} from "string-apostrophes";
assert.deepEqual(
convertAll(`In the '60s, rock 'n' roll`, {
convertApostrophes: 1,
convertEntities: 0,
}),
{
result: "In the ’60s, rock ’n’ roll",
ranges: [
[7, 8, "’"],
[18, 21, "’n’"],
],
}
);
assert.deepEqual(
convertOne(`test's`, {
from: 4,
to: 5,
convertApostrophes: true,
convertEntities: true,
}),
[[4, 5, "’"]]
);
§ Idea
As you know, straight apostrophes are not always typographically-correct: John's
should be John’s
, with right single quote instead of apostrophe .
This program converts all cases of single and double apostrophes, plus primes .
Sources used in rules logic and unit tests:
- Oxford A-Z of Grammar and Punctuation 2nd Ed., 2009, ISBN 978-0199564675
- Butterick's Practical Typography 2nd Ed., "Apostrophes" chapter
§ API
When you consume this package,
// Common JS:
const { convertOne, convertAll } = require("string-apostrophes");
// ES Modules:
import { convertOne, convertAll } from "string-apostrophes";
you get two functions: convertAll()
and convertOne()
.
§ convertAll()
convertAll
is a function; its API is the following:
Input argument | Key value's type | Obligatory? | Description |
---|---|---|---|
str | String | yes | String which we will process |
opts | Plain object | no | Put options here |
For example,
console.log(convertAll(`test's`, {
convertApostrophes: true,
convertEntities: true
})).result,
// => "test’s"
§ Output
A plain object is returned:
Returned object's key | The type of its value | Description |
---|---|---|
result | String | Processed string, with all ranges applied |
ranges | Array of zero or more arrays | Ranges that were gathered and applied to produce result |
For example, if you gave string In the '60s, rock 'n' roll
with apostrophes, the result on default settings would be:
{
result: `In the ’60s, rock ’n’ roll`,
ranges: [
[7, 8, `’`],
[18, 19, `’`],
[20, 21, `’`]
]
}
§ Options Object, opts
Options Object's key | The type of its value | Default | Obligatory? | Description |
---|---|---|---|---|
convertEntities | Boolean | false | no | Should we HTML-encode the characters? |
convertApostrophes | Boolean | true | no | Killswitch. If it's false , the program does nothing. |
§ convertOne()
convertOne
is a function; its API is the following:
Input argument | Key value's type | Obligatory? | Description |
---|---|---|---|
str | String | yes | String which we will process |
opts | Plain object | yes | Put options here |
opts.from
is obligatory — that's how you tell the program which characters to process.
For example:
console.log(convertOne(`test's`, {
from: 4,
to: 5,
convertApostrophes: true,
convertEntities: false
})),
// => [[4, 5, "’"]]
§ Output
It returns an array of zero or more arrays (ranges), each representing what needs to be done.
For example, result [[2, 3, "‘"], [5, 6, "’"]]
means "replace string chunk from index 2
to 3
with ‘
" and from index 5
to 6
with ’
. You can use ranges-apply
to process a string using those ranges (in other words, "to apply those ranges").
§ Options Object, opts
Options Object's key | The type of its value | Default | Obligatory? | Description |
---|---|---|---|---|
from | Natural number, string index | undefined | yes | Where does the character we need to process start in a given index? |
to | Natural number, string index | from + 1 | no | Where does the character we need to process end in a given index? |
value | String | undefined | no | Override the value of a string value, present at str.slice(from, to) |
convertEntities | Boolean | false | no | Should we HTML-encode the characters? |
convertApostrophes | Boolean | true | no | Killswitch. If it's false , the program does nothing. |
offsetBy | Function | undefined | no | If you provide a function, it will be called with a natural number input argument, meaning how much characters to skip next. |
§ opts.offsetBy
Offset is needed to bypass characters we already fixed — it happens for example, with nested quotes - we'd fix many in one go, and we need to skip the further processing; otherwise, those characters would get processed multiple times.
For example, here's how the convertAll()
index is bumped using offsetBy
, in a callback-fashion:
function convertAll(str, opts) {
let ranges = [];
const preppedOpts = Object.assign({}, opts);
// loop through the given string
for (let i = 0, len = str.length; i < len; i++) {
// define starting index:
preppedOpts.from = i;
// offset function:
preppedOpts.offsetBy = (idx) => {
i = i + idx;
};
// calculate the result:
const res = convertOne(str, preppedOpts);
if (Array.isArray(res) && res.length) {
ranges = ranges.concat(res);
}
}
return {
result: rangesApply(str, ranges),
ranges,
};
}
The inner function convertOne()
bumps outer's convertAll()
index.
§ opts.value
Consider string Your's
with HTML-escaped apostrophe:
Your's
There are various other cases of apostrophes and quotes where we have a sentence, and all apostrophes/quotes are there, and we know where just different character(s) represent them. Values are not '
and "
.
We are not going to code up all those cases!
Instead, use convertOne()
, process each "symbol" one-by-one and instruct the program from where (from
) to where (to
) is a particular character (value
).
For example,
const { convertOne, convertAll } = require("string-apostrophes");
const res = convertOne(`test's`, {
from: 4,
to: 10,
value: "'", // <-------- we insist to program that it's an apostrophe between indexes 4 and 10
convertEntities: 0,
});
console.log(JSON.stringify(res, null, 0));
// => [[4, 10, "’"]]
In the example above, the program evaluates surroundings of '
as if it was a "normal" apostrophe and suggests a replacement.
In practice, that's how detergent
uses this package.
§ Compared to Others
This program,string-apostrophes | straight-to-curly-quotes | smartquotes | typographic-quotes | |
---|---|---|---|---|
Returns processed string | ✅ | ✅ | ✅ | ✅ |
Additionally returns index ranges | ✅ | ❌ | ❌ | ❌ |
Replaces quotes in DOM, on a web page, where you put a script in | ❌ | ❌ | ✅ | ❌ |
Not regex-based | ✅ | ❌ | ❌ | ❌ |
Can output HTML-encoded content upon request | ✅ | ❌ | ❌ | ❌ |
Killswitch to bypass processing | ✅ | ❌ | ❌ | ❌ |
Allows to process any part of string as if it were single or double quote | ✅ | ❌ | ❌ | ❌ |
CommonJS (require() ) and ES Modules (in ES6+, import ) builds | ✅ | ❌ | ❌ | ❌ |
UMD builds published to npm and available from unpkg or jsdelivr | ✅ | ❌ | ❌ | ❌ |
Serves other languages besides English | ❌ | ❌ | ❌ | ✅ |
This program has string-in, string-out type API; the DOM changes capabilities are not bundled because browser is only one of many possible targets of an npm program. Other consuming programs might not even have DOM or consumers might be Electron or whatever. It's best to write other, standalone apps which use API-like core function and work from original (string-in string-out), "API" package.
§ Changelog
See it in the monorepo , on Sourcehut.
§ Licence
Copyright © 2010–2020 Roy Revelt and other contributors