string-apostrophes2.0.1

Comprehensive, HTML-entities-aware tool to typographically-correct the apostrophes and single/double quotes

Quick Take

import { strict as assert } from "assert";
import {
  convertOne,
  convertAll,
} from "string-apostrophes";

assert.deepEqual(
  convertAll(`In the '60s, rock 'n' roll`, {
    convertApostrophes: true,
    convertEntities: false,
  }),
  {
    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 opens in a new tab instead of apostrophe opens in a new tab.

This program converts all cases of single and double apostrophes, plus primes opens in a new tab.

Sources used in rules logic and unit tests:

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&apos;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&apos;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 &apos; 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 opens in a new tab smartquotes opens in a new tab typographic-quotes opens in a new tab
npm link npm link opens in a new tab npm link opens in a new tab npm link opens in a new tab
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 opens in a new tab, on GitHub.

Contributing

To report bugs or request features or assistance, raise an issue on GitHub opens in a new tab.

Any code contributions welcome! All Pull Requests will be dealt promptly.

Licence

MIT opens in a new tab

Copyright © 2010–2021 Roy Revelt and other contributors

Related packages:

📦 detergent 8.0.1
Extracts, cleans and encodes text
📦 string-extract-class-names 7.0.1
Extracts CSS class/id names from a string
📦 string-remove-thousand-separators 6.0.1
Detects and removes thousand separators (dot/comma/quote/space) from string-type digits
📦 string-collapse-white-space 10.0.1
Replace chunks of whitespace with a single spaces
📦 string-process-comma-separated 3.0.1
Extracts chunks from possibly comma or whatever-separated string
📦 string-collapse-leading-whitespace 6.0.1
Collapse the leading and trailing whitespace of a string
📦 string-match-left-right 8.0.1
Match substrings on the left or right of a given index, ignoring whitespace