1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
|
--- Readability.js.orig 2025-05-07 21:45:49.710343570 +0200
+++ Readability.js 2025-05-08 06:50:13.641779085 +0200
@@ -2206,6 +2206,81 @@
},
/**
+ /* getReadingSpeedForLanguage and _get_reading_time from Firefox mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview.js
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/
+ */
+
+ /**
+ * Returns the reading speed of a selection of languages with likely variance.
+ *
+ * Reading speed estimated from a study done on reading speeds in various languages.
+ * study can be found here: http://iovs.arvojournals.org/article.aspx?articleid=2166061
+ *
+ * @return object with characters per minute and variance. Defaults to English
+ * if no suitable language is found in the collection.
+ */
+ getReadingSpeedForLanguage(lang) {
+ const readingSpeed = new Map([
+ ["en", { cpm: 987, variance: 118 }],
+ ["ar", { cpm: 612, variance: 88 }],
+ ["de", { cpm: 920, variance: 86 }],
+ ["es", { cpm: 1025, variance: 127 }],
+ ["fi", { cpm: 1078, variance: 121 }],
+ ["fr", { cpm: 998, variance: 126 }],
+ ["he", { cpm: 833, variance: 130 }],
+ ["it", { cpm: 950, variance: 140 }],
+ ["ja", { cpm: 357, variance: 56 }],
+ ["nl", { cpm: 978, variance: 143 }],
+ ["pl", { cpm: 916, variance: 126 }],
+ ["pt", { cpm: 913, variance: 145 }],
+ ["ru", { cpm: 986, variance: 175 }],
+ ["sl", { cpm: 885, variance: 145 }],
+ ["sv", { cpm: 917, variance: 156 }],
+ ["tr", { cpm: 1054, variance: 156 }],
+ ["zh", { cpm: 255, variance: 29 }],
+ ]);
+
+ return readingSpeed.has(lang)
+ ? [readingSpeed.get(lang), lang]
+ : [readingSpeed.get("en"), "en"];
+ },
+
+ _get_reading_time: function(length, lang = "en") {
+ const [readingSpeed, readingSpeedLang] = this.getReadingSpeedForLanguage(lang);
+ const charactersPerMinuteLow = readingSpeed.cpm - readingSpeed.variance;
+ const charactersPerMinuteHigh = readingSpeed.cpm + readingSpeed.variance;
+ const readingTimeMinsSlow = Math.ceil(length / charactersPerMinuteLow);
+ const readingTimeMinsFast = Math.ceil(length / charactersPerMinuteHigh);
+
+ // Construct a localized and "humanized" reading time in minutes.
+ // If we have both a fast and slow reading time we'll show both e.g.
+ // "2 - 4 minutes", otherwise we'll just show "4 minutes".
+ try {
+ var parts = new Intl.RelativeTimeFormat(readingSpeedLang).formatToParts(
+ readingTimeMinsSlow,
+ "minute"
+ );
+ if (parts.length == 3) {
+ // No need to use part[0] which represents the literal "in".
+ var readingTime = parts[1].value; // reading time in minutes
+ var minutesLiteral = parts[2].value; // localized singular or plural literal of 'minute'
+ var readingTimeString = `${readingTime} ${minutesLiteral}`;
+ if (readingTimeMinsSlow != readingTimeMinsFast) {
+ readingTimeString = `${readingTimeMinsFast} - ${readingTimeString}`;
+ }
+ return readingTimeString;
+ }
+ } catch (error) {
+ console.error(`Failed to format reading time: ${error}`);
+ }
+
+ return "";
+ },
+
+ /**
* Runs readability.
*
* Workflow:
@@ -2267,7 +2342,8 @@
textContent: textContent,
length: textContent.length,
excerpt: metadata.excerpt,
- siteName: metadata.siteName || this._articleSiteName
+ siteName: metadata.siteName || this._articleSiteName,
+ reading_time: this._get_reading_time(textContent.length, this._doc.lang),
};
}
};
|