├── .gitignore ├── LICENSE.md ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── Providers │ └── ReadTimeServiceProvider.php ├── ReadTime.php ├── config │ └── read-time.php ├── helpers.php └── resources │ └── lang │ ├── bg │ └── read-time.php │ ├── ca │ └── read-time.php │ ├── de │ └── read-time.php │ ├── en │ └── read-time.php │ ├── es │ └── read-time.php │ ├── fr │ └── read-time.php │ ├── nl │ └── read-time.php │ ├── ru │ └── read-time.php │ ├── tr │ └── read-time.php │ └── uk │ └── read-time.php └── tests └── ReadTimeTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | vendor 3 | .DS_Store 4 | .idea -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Mark Townsend 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A PHP package to show users how long it takes to read content. 2 | 3 |

4 | 5 |

6 | 7 | ## Installation 8 | 9 | Install via composer: 10 | 11 | 12 | ``` 13 | composer require mtownsend/read-time 14 | ``` 15 | 16 | 17 | *This package is designed to work with any PHP 7.0+ application but has special support for Laravel.* 18 | 19 | ### Registering the service provider (Laravel) 20 | 21 | For Laravel 5.4 and lower, add the following line to your ``config/app.php``: 22 | 23 | ```php 24 | /* 25 | * Package Service Providers... 26 | */ 27 | Mtownsend\ReadTime\Providers\ReadTimeServiceProvider::class, 28 | ``` 29 | 30 | For Laravel 5.5 and greater, the package will auto register the provider for you. 31 | 32 | ### Using Lumen 33 | 34 | To register the service provider, add the following line to ``app/bootstrap/app.php``: 35 | 36 | ```php 37 | $app->register(Mtownsend\ReadTime\Providers\ReadTimeServiceProvider::class); 38 | ``` 39 | 40 | ### Publishing the config file (Laravel) 41 | 42 | ReadTime has special configuration support for Laravel applications. You can publish a single config file and customize how you want your read time text to be displayed. 43 | 44 | ```` 45 | php artisan vendor:publish --provider="Mtownsend\ReadTime\Providers\ReadTimeServiceProvider" --tag="read-time-config" 46 | ```` 47 | 48 | These are the contents of the ``read-time.php`` config file: 49 | 50 | ```php 51 | return [ 52 | 53 | /* 54 | * Whether or not minute/second should be abbreviated as min/sec 55 | */ 56 | 'abbreviate_time_measurements' => false, 57 | 58 | /* 59 | * Omit seconds from being displayed in the read time estimate 60 | */ 61 | 'omit_seconds' => true, 62 | 63 | /* 64 | * Whether or not only the time should be displayed 65 | */ 66 | 'time_only' => false, 67 | 68 | /* 69 | * The average words per minute reading time 70 | */ 71 | 'words_per_minute' => 230, 72 | ]; 73 | ``` 74 | 75 | ### Publishing the translation files (Laravel) 76 | 77 | ReadTime supports localization with Laravel. If you are using Laravel you'll likely want to use the premade translations. 78 | 79 | ```` 80 | php artisan vendor:publish --provider="Mtownsend\ReadTime\Providers\ReadTimeServiceProvider" --tag="read-time-language-files" 81 | ```` 82 | 83 | ## Quick start 84 | 85 | ### Using the class 86 | 87 | Here is an example of the most basic usage: 88 | 89 | ```php 90 | use Mtownsend\ReadTime\ReadTime; 91 | 92 | $readTime = (new ReadTime($content))->get(); 93 | ``` 94 | 95 | You may also pass several arguments to the constructor if you wish to change settings on the fly: 96 | 97 | ```php 98 | use Mtownsend\ReadTime\ReadTime; 99 | 100 | $readTime = (new ReadTime($content, $omitSeconds = true, $abbreviated = false, $wordsPerMinute = 230))->get(); 101 | // or 102 | $readTime = (new ReadTime($content)) 103 | ->omitSeconds() 104 | ->abbreviated() 105 | ->wpm($wordsPerMinute) 106 | ->get(); 107 | ``` 108 | 109 | The ReadTime class is able to accept a string of content or a flat array of multiple pieces of content. This may come in handy if you are attempting to display the total read time of body content along with sidebar content. For example: 110 | 111 | ```php 112 | use Mtownsend\ReadTime\ReadTime; 113 | 114 | $readTime = (new ReadTime([$content, $moreContent, $evenMoreContent]))->get(); 115 | ``` 116 | 117 | ## Methods, and arguments 118 | 119 | **Method** 120 | 121 | ``->abbreviated(bool $abbreviated = true)`` 122 | 123 | Abbreviate the words 'minute' and 'second' to 'min' and 'sec'. 124 | 125 | ``->get()`` 126 | 127 | Retrieve the read time. *Note: you may also invoke the class as a function or cast it as a string to retrieve the same result as ``get()``.* 128 | 129 | ``->getTranslation($key = null)`` 130 | 131 | You may return the current translation array the class is using by omitting any argument from this method or get a specific translation key by passing it as an argument. 132 | 133 | ``->ltr(bool $ltr = true)`` 134 | 135 | Set the text direction of the read time result to left (default) with true, and ``right`` with ``false``. Alternatively, you may simply call the ``->rtl()`` method without any argument. 136 | 137 | ``->omitSeconds(bool $omitSeconds = true)`` 138 | 139 | Have the read time display omit seconds. Pass ``false`` to include seconds. 140 | 141 | ``->rtl(bool $rtl = true)`` 142 | 143 | Set the text direction of the read time result to right (default) with true, and ``left`` with ``false``. Alternatively, you may simply call the ``->ltr()`` method without any argument. 144 | 145 | ``->setTranslation(array $translations)`` 146 | 147 | Manually set the translation text for the class to use. If no key is passed it will default to the English counterpart. A complete translation array will contain the following: 148 | 149 | ```php 150 | [ 151 | 'reads_left_to_right' => true, 152 | 'min' => 'min', 153 | 'minute' => 'minute', 154 | 'sec' => 'sec', 155 | 'second' => 'second', 156 | 'read' => 'read' 157 | ] 158 | ``` 159 | 160 | ``->timeOnly(bool $timeOnly = true)`` 161 | 162 | Omit any words from the read time result. Pass ``false`` to include words. 163 | 164 | ``->toArray()`` 165 | 166 | Get the contents and settings of the class as an array. 167 | 168 | ``->toJson()`` 169 | 170 | Get the contents and settings of the class as a json string. 171 | 172 | ``->wpm(int $wordsPerMinute)`` 173 | 174 | Set the average pace of words read per minute. 175 | 176 | ### Using the global helper (Laravel) 177 | 178 | If you are using Laravel, this package provides a convenient helper function which is globally accessible. 179 | 180 | ```php 181 | read_time($content); 182 | ``` 183 | 184 | The global helper is exceptionally useful in your Laravel application. It can be used in views (remember, it outputs the read time if invoked or cast as a string, which Blade's double curly braces does): 185 | 186 | ```` 187 |

Some blog title

188 | {{ read_time($content) }} 189 |
190 | ```` 191 | 192 | The global helper will also attempt to intelligently detect the information you are passing it. For example, if you pass it a **non-associative array** it will assume you are passing an array of content. 193 | 194 | ```` 195 |

Some blog title

196 | {{ read_time([$content, $moreContent]) }} 197 |
198 | ```` 199 | 200 | But you are still free to customize the ReadTime class settings on the fly using the global helper. Simply pass an associative array of settings: 201 | 202 | ```` 203 | {{ read_time([ 204 | 'content' => $content, 205 | // or 206 | // 'content' => [$content, $moreContent], 207 | 'omit_seconds' => true, 208 | 'time_only' => false, 209 | 'abbreviated' => true, 210 | 'words_per_minute' => 230, 211 | 'ltr' => true, 212 | 'translation' => [ 213 | 'reads_left_to_right' => true, 214 | 'min' => 'min', 215 | 'minute' => 'minute', 216 | 'sec' => 'sec', 217 | 'second' => 'second', 218 | 'read' => 'read' 219 | ] 220 | ]) }} 221 | ```` 222 | 223 | ## Purpose 224 | 225 | Sites like Medium.com have popularized the concept of giving users an estimate for the amount of time it will take them to read the content. With this convenience in mind, ReadTime gives PHP developers the same tool for their readable content. It's a simple feature that will give a nice touch to your PHP application. 226 | 227 | ## Contributing translations 228 | 229 | Pull requests for translations are encouraged. Please be sure to follow the existing format. 230 | 231 | ## Credits 232 | 233 | - Mark Townsend 234 | - [All Contributors](../../contributors) 235 | 236 | ## Testing 237 | 238 | You can run the tests with: 239 | 240 | ```bash 241 | ./vendor/bin/phpunit 242 | ``` 243 | 244 | ## License 245 | 246 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 247 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mtownsend/read-time", 3 | "description": "A PHP package to show users how long it takes to read content.", 4 | "keywords": [ 5 | "read", 6 | "time", 7 | "minutes", 8 | "reading", 9 | "content", 10 | "medium", 11 | "min", 12 | "blog", 13 | "article" 14 | ], 15 | "license": "MIT", 16 | "authors": [ 17 | { 18 | "name": "Mark Townsend", 19 | "email": "mtownsend5512@gmail.com", 20 | "role": "Developer" 21 | } 22 | ], 23 | "autoload": { 24 | "psr-4": { 25 | "Mtownsend\\ReadTime\\": "src" 26 | }, 27 | "files": [ 28 | "src/helpers.php" 29 | ] 30 | }, 31 | "require": { 32 | "php": "^7.4|^8.0" 33 | }, 34 | "require-dev": { 35 | "phpunit/phpunit": "^9.3" 36 | }, 37 | "autoload-dev": { 38 | "psr-4": { 39 | "Mtownsend\\ReadTime\\Test\\": "tests/" 40 | } 41 | }, 42 | "extra": { 43 | "laravel": { 44 | "providers": [ 45 | "Mtownsend\\ReadTime\\Providers\\ReadTimeServiceProvider" 46 | ] 47 | } 48 | }, 49 | "minimum-stability": "stable" 50 | } 51 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | src/ 9 | 10 | 11 | 12 | 13 | tests 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Providers/ReadTimeServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadTranslationsFrom(__DIR__.'/../resources/lang/', 'read-time'); 20 | 21 | $this->publishes([ 22 | __DIR__ . '/../config/read-time.php' => config_path('read-time.php') 23 | ], 'read-time-config'); 24 | 25 | $this->publishes([ 26 | __DIR__ . "/../resources/lang/" => resource_path("lang/vendor/read-time") 27 | ], 'read-time-language-files'); 28 | 29 | } 30 | 31 | /** 32 | * Register any application services. 33 | * 34 | * @return void 35 | */ 36 | public function register() 37 | { 38 | $this->app->bind('read_time', function ($app, $data) { 39 | if (!isset($data['content'])) { 40 | throw new Exception('Content must be supplied to ReadTime class'); 41 | } 42 | 43 | $this->mergeConfigFrom(__DIR__.'/../config/read-time.php', 'read-time'); 44 | 45 | $content = $data['content']; 46 | $omitSeconds = isset($data['omit_seconds']) ? $data['omit_seconds'] : config('read-time.omit_seconds'); 47 | $timeOnly = isset($data['time_only']) ? $data['time_only'] : config('read-time.time_only'); 48 | $abbreviated = isset($data['abbreviated']) ? $data['abbreviated'] : config('read-time.abbreviate_time_measurements'); 49 | $wordsPerMinute = isset($data['words_per_minute']) ? $data['words_per_minute'] : config('read-time.words_per_minute'); 50 | $ltr = isset($data['ltr']) ? $data['ltr'] : __('read-time.reads_left_to_right'); 51 | $translation = isset($data['translation']) ? $data['translation'] : __('read-time::read-time'); 52 | 53 | return (new ReadTime($content)) 54 | ->omitSeconds($omitSeconds) 55 | ->timeOnly($timeOnly) 56 | ->abbreviated($abbreviated) 57 | ->wpm($wordsPerMinute) 58 | ->ltr($ltr) 59 | ->setTranslation($translation); 60 | }); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/ReadTime.php: -------------------------------------------------------------------------------- 1 | abbreviated = $abbreviated; 74 | $this->content = $this->parseContent($content); 75 | $this->ltr = true; 76 | $this->timeOnly = false; 77 | $this->omitSeconds = $omitSeconds; 78 | $this->defaultTranslations(); 79 | $this->wordsInContent = (int) str_word_count($this->content); 80 | $this->wordsPerMinute = (int) $wordsPerMinute; 81 | } 82 | 83 | public function __toString() 84 | { 85 | return $this->get(); 86 | } 87 | 88 | public function __invoke() 89 | { 90 | return $this->get(); 91 | } 92 | 93 | /** 94 | * Abbreviate the minutes/seconds 95 | * 96 | * @param bool $abbreviated 97 | * @return \Mtownsend\ReadTime\ReadTime 98 | */ 99 | public function abbreviated($abbreviated = true) 100 | { 101 | $this->abbreviated = $abbreviated; 102 | return $this; 103 | } 104 | 105 | /** 106 | * Calculate the reading time for minutes 107 | * 108 | * @return int 109 | */ 110 | protected function calculateMinutes(): int 111 | { 112 | $minutes = floor($this->wordsInContent / $this->wordsPerMinute); 113 | return (int) $minutes < 1 ? 1 : $minutes; 114 | } 115 | 116 | /** 117 | * Calculate the reading time for seconds 118 | * 119 | * @return int 120 | */ 121 | protected function calculateSeconds(): int 122 | { 123 | return (int) floor($this->wordsInContent % $this->wordsPerMinute / ($this->wordsPerMinute / 60)); 124 | } 125 | 126 | /** 127 | * Strip html tags from content 128 | * 129 | * @param string $content 130 | * @return string 131 | */ 132 | protected function cleanContent($content): string 133 | { 134 | return strip_tags($content); 135 | } 136 | 137 | /** 138 | * Remove any double spaces or post/prefixed spaces 139 | * @param string $string 140 | * @return string 141 | */ 142 | protected function cleanReadTimeString($string): string 143 | { 144 | return trim(preg_replace('/\s+/u', ' ', $string)); 145 | } 146 | 147 | /** 148 | * Set the default translation when the class is instantiated 149 | * 150 | * @return void 151 | */ 152 | protected function defaultTranslations() 153 | { 154 | $this->setTranslation([]); 155 | } 156 | 157 | /** 158 | * Set the estimate property 159 | * 160 | * @return void 161 | */ 162 | protected function estimate() 163 | { 164 | $this->estimate = [ 165 | 'minutes' => $this->calculateMinutes(), 166 | 'seconds' => $this->omitSeconds ? 0 : $this->calculateSeconds(), 167 | 'read_time' => $this->formatReadTime() 168 | ]; 169 | } 170 | 171 | /** 172 | * Return the formatted read time string based on the set properties 173 | * 174 | * @return string 175 | */ 176 | protected function formatReadTime(): string 177 | { 178 | $minuteTime = $this->calculateMinutes(); 179 | $secondTime = $this->calculateSeconds(); 180 | $message = ''; 181 | 182 | $minutes = $this->abbreviated ? $this->getTranslation('min') : $this->getTranslation('minute'); 183 | 184 | $message .= "$minuteTime $minutes"; 185 | 186 | if ($this->omitSeconds || !$secondTime) { 187 | $seconds = ''; 188 | } else { 189 | $seconds = $this->abbreviated ? $this->getTranslation('sec') : $this->getTranslation('second'); 190 | $message .= " $secondTime $seconds"; 191 | } 192 | 193 | $message = $this->timeOnly ? $this->cleanReadTimeString($message) : $this->cleanReadTimeString("$message {$this->translations['read']}"); 194 | 195 | if ($this->ltr == false) { 196 | $message = $this->reverseWords($message); 197 | } 198 | 199 | return $message; 200 | } 201 | 202 | /** 203 | * Return the formatted read time string 204 | * 205 | * @return string 206 | */ 207 | public function get(): string 208 | { 209 | $this->estimate(); 210 | return $this->estimate['read_time']; 211 | } 212 | 213 | /** 214 | * Get the translation array or specific key 215 | * 216 | * @param string|null $key The translation key 217 | * @return mixed array if no key is passed, or string if existing key is passed 218 | */ 219 | public function getTranslation($key = null) 220 | { 221 | return is_null($key) ? $this->translations : $this->translations[$key]; 222 | } 223 | 224 | /** 225 | * Check if the given content is formatted appropriately 226 | * 227 | * @param mixed $content 228 | * @return bool 229 | */ 230 | protected function invalidContent($content): bool 231 | { 232 | if (is_array($content) || is_string($content)) { 233 | return false; 234 | } 235 | return true; 236 | } 237 | 238 | /** 239 | * Set ltr mode for the read time 240 | * 241 | * @param bool 242 | * @return \Mtownsend\ReadTime\ReadTime 243 | */ 244 | public function ltr(bool $ltr = true) 245 | { 246 | $this->ltr = $ltr; 247 | return $this; 248 | } 249 | 250 | /** 251 | * Omit seconds from being displayed in the read time result 252 | * 253 | * @param bool $omitSeconds 254 | * @return \Mtownsend\ReadTime\ReadTime 255 | */ 256 | public function omitSeconds(bool $omitSeconds = true) 257 | { 258 | $this->omitSeconds = $omitSeconds; 259 | return $this; 260 | } 261 | 262 | /** 263 | * Parse the given content so it can be output as a read time 264 | * 265 | * @param mixed $receivedContent String or array of content 266 | * @return string 267 | */ 268 | protected function parseContent($receivedContent) 269 | { 270 | if ($this->invalidContent($receivedContent)) { 271 | throw new Exception('Content must be type of array or string'); 272 | } 273 | 274 | if (is_array($receivedContent)) { 275 | $content = ''; 276 | foreach ($receivedContent as $item) { 277 | if (is_array($item)) { 278 | $item = $this->parseContent($item); 279 | } 280 | $content .= trim($item); 281 | } 282 | } else { 283 | $content = $receivedContent; 284 | } 285 | 286 | return $this->cleanContent($content); 287 | } 288 | 289 | /** 290 | * Reverse the words in a string 291 | * 292 | * @param string $string 293 | * @return string 294 | */ 295 | protected function reverseWords($string): string 296 | { 297 | return implode(' ', array_reverse(explode(' ', $string))); 298 | } 299 | 300 | /** 301 | * Set the read time results to read from right to left 302 | * 303 | * @param bool $rtl 304 | * @return Mtownsend\ReadTime\ReadTime 305 | */ 306 | public function rtl(bool $rtl = true) 307 | { 308 | $this->ltr = $rtl ? false : true; 309 | return $this; 310 | } 311 | 312 | /** 313 | * Set the translation keys for the read time string 314 | * 315 | * @param array $translations An associative array of translation text 316 | * @return \Mtownsend\ReadTime\ReadTime 317 | */ 318 | public function setTranslation(array $translations) 319 | { 320 | $this->translations = [ 321 | 'min' => isset($translations['min']) ? $translations['min'] : 'min', 322 | 'minute' => isset($translations['minute']) ? $translations['minute'] : 'minute', 323 | 'sec' => isset($translations['sec']) ? $translations['sec'] : 'sec', 324 | 'second' => isset($translations['second']) ? $translations['second'] : 'second', 325 | 'read' => isset($translations['read']) ? $translations['read'] : 'read' 326 | ]; 327 | return $this; 328 | } 329 | 330 | /** 331 | * Determine if any text should accompany the time in the read time 332 | * 333 | * @param bool $timeOnly 334 | * @return \Mtownsend\ReadTime\ReadTime 335 | */ 336 | public function timeOnly(bool $timeOnly = true) 337 | { 338 | $this->timeOnly = $timeOnly; 339 | return $this; 340 | } 341 | 342 | /** 343 | * Return an array of the class data 344 | * 345 | * @return array 346 | */ 347 | public function toArray(): array 348 | { 349 | $this->estimate(); 350 | return array_merge($this->estimate, [ 351 | 'abbreviated' => (bool) $this->abbreviated, 352 | 'left_to_right' => (bool) $this->ltr, 353 | 'omit_seconds' => (bool) $this->omitSeconds, 354 | 'time_only' => (bool) $this->timeOnly, 355 | 'translation' => $this->translations, 356 | 'words_in_content' => (int) $this->wordsInContent, 357 | 'words_per_minute' => (int) $this->wordsPerMinute 358 | ]); 359 | } 360 | 361 | /** 362 | * Return a json string of the class data 363 | * 364 | * @return string 365 | */ 366 | public function toJson(): string 367 | { 368 | return json_encode($this->toArray()); 369 | } 370 | 371 | /** 372 | * Set the average words read per minute 373 | * 374 | * @param int $wordsPerMinute 375 | * @return \Mtownsend\ReadTime\ReadTime 376 | */ 377 | public function wpm(int $wordsPerMinute) 378 | { 379 | $this->wordsPerMinute = $wordsPerMinute; 380 | return $this; 381 | } 382 | } 383 | -------------------------------------------------------------------------------- /src/config/read-time.php: -------------------------------------------------------------------------------- 1 | false, 9 | 10 | /* 11 | * Omit seconds from being displayed in the read time estimate 12 | */ 13 | 'omit_seconds' => true, 14 | 15 | /* 16 | * Whether or not only the time should be displayed 17 | */ 18 | 'time_only' => false, 19 | 20 | /* 21 | * The average words per minute reading time 22 | */ 23 | 'words_per_minute' => 230, 24 | ]; -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | $data]; 14 | } 15 | // An array is passed that is not associative, assume it is multiple pieces of content 16 | elseif (is_array($data) && !is_assoc_array($data)) { 17 | $data = ['content' => $data]; 18 | } 19 | return app()->makeWith('read_time', $data); 20 | } 21 | } 22 | 23 | if (!function_exists('is_assoc_array')) { 24 | /** 25 | * Determine if an array is associative or sequential 26 | * 27 | * @return bool 28 | */ 29 | function is_assoc_array($array) 30 | { 31 | return array_keys($array) !== range(0, count($array) - 1); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/resources/lang/bg/read-time.php: -------------------------------------------------------------------------------- 1 | true, 5 | 'min' => 'мин', 6 | 'minute' => 'минута', 7 | 'sec' => 'сек', 8 | 'second' => 'секунда', 9 | 'read' => 'четене' 10 | ]; 11 | -------------------------------------------------------------------------------- /src/resources/lang/ca/read-time.php: -------------------------------------------------------------------------------- 1 | true, 5 | 'min' => 'min', 6 | 'minute' => 'minut', 7 | 'sec' => 'seg', 8 | 'second' => 'segon', 9 | 'read' => 'llegir' 10 | ]; 11 | -------------------------------------------------------------------------------- /src/resources/lang/de/read-time.php: -------------------------------------------------------------------------------- 1 | true, 5 | 'min' => 'min', 6 | 'minute' => 'minute', 7 | 'sec' => 'sek', 8 | 'second' => 'sekunde', 9 | 'read' => 'lesen' 10 | ]; 11 | -------------------------------------------------------------------------------- /src/resources/lang/en/read-time.php: -------------------------------------------------------------------------------- 1 | true, 5 | 'min' => 'min', 6 | 'minute' => 'minute', 7 | 'sec' => 'sec', 8 | 'second' => 'second', 9 | 'read' => 'read' 10 | ]; 11 | -------------------------------------------------------------------------------- /src/resources/lang/es/read-time.php: -------------------------------------------------------------------------------- 1 | true, 5 | 'min' => 'min', 6 | 'minute' => 'minuto', 7 | 'sec' => 'seg', 8 | 'second' => 'segundo', 9 | 'read' => 'leer' 10 | ]; 11 | -------------------------------------------------------------------------------- /src/resources/lang/fr/read-time.php: -------------------------------------------------------------------------------- 1 | true, 5 | 'min' => 'min', 6 | 'minute' => 'minute', 7 | 'sec' => 'sec', 8 | 'second' => 'seconde', 9 | 'read' => 'lire' 10 | ]; 11 | -------------------------------------------------------------------------------- /src/resources/lang/nl/read-time.php: -------------------------------------------------------------------------------- 1 | true, 5 | 'min' => 'min', 6 | 'minute' => 'minuut', 7 | 'sec' => 'sec', 8 | 'second' => 'seconde', 9 | 'read' => 'leestijd' 10 | ]; 11 | -------------------------------------------------------------------------------- /src/resources/lang/ru/read-time.php: -------------------------------------------------------------------------------- 1 | true, 5 | 'min' => 'мин.', 6 | 'minute' => 'минут', 7 | 'sec' => 'сек.', 8 | 'second' => 'секунд', 9 | 'read' => 'чтения' 10 | ]; 11 | -------------------------------------------------------------------------------- /src/resources/lang/tr/read-time.php: -------------------------------------------------------------------------------- 1 | true, 5 | 'min' => 'dk', 6 | 'minute' => 'dakika', 7 | 'sec' => 'sn', 8 | 'second' => 'saniye', 9 | 'read' => 'okuma süresi' 10 | ]; 11 | -------------------------------------------------------------------------------- /src/resources/lang/uk/read-time.php: -------------------------------------------------------------------------------- 1 | true, 5 | 'min' => 'хв.', 6 | 'minute' => 'хвилин', 7 | 'sec' => 'сек.', 8 | 'second' => 'секунд', 9 | 'read' => 'читання' 10 | ]; 11 | -------------------------------------------------------------------------------- /tests/ReadTimeTest.php: -------------------------------------------------------------------------------- 1 | mainContent = <<Chicken Enchiladas 20 | By Leroy Jenkins 21 |

22 | Lorem ipsum dolor sit amet, in sint erat intellegebat sit, accumsan maluisset mea ei. At wisi omnis usu, has in tibique singulis temporibus. Mel ne aeque oblique, per id probatus imperdiet. Per tale legimus facilisis ex. 23 |

24 |

25 | Sonet dolor honestatis mel ne. Agam salutandi ut ius. Liber euismod vivendo id est, vim eu putent adipisci salutandi. Graeco habemus vix at, in eam suas habeo. Eu mel denique disputationi, ius ut idque repudiandae, his ex habeo petentium definiebas. 26 |

27 |
    28 |
  1. 29 | Coat large saute pan with oil. Season chicken with salt and pepper. Brown chicken over medium heat, allow 7 minutes each side or until no longer pink. Sprinkle chicken with cumin, garlic powder and Mexican spices before turning. Remove chicken to a platter, allow to cool. 30 |
  2. 31 |
  3. 32 | Saute onion and garlic in chicken drippings until tender. Add corn and chiles. Stir well to combine. Add canned tomatoes, saute 1 minute. 33 |
  4. 34 |
  5. 35 | Pull chicken breasts apart by hand into shredded strips. Add shredded chicken to saute pan, combine with vegetables. Dust the mixture with flour to help set. 36 |
  6. 37 |
  7. 38 | Microwave tortillas on high for 30 seconds. This softens them and makes them more pliable. Coat the bottom of 2 (13 by 9-inch) pans with a ladle of enchilada sauce. Using a large shallow bowl, dip each tortilla in enchilada sauce to lightly coat. Spoon 1/4 cup chicken mixture in each tortilla. Fold over filling, place 8 enchiladas in each pan with seam side down. Top with remaining enchilada sauce and cheese. 39 |
  8. 40 |
  9. 41 | Bake for 15 minutes in a preheated 350 degree F oven until cheese melts. Garnish with cilantro, scallion, sour cream and chopped tomatoes before serving. Serve with Spanish rice and beans. 42 |
  10. 43 |
44 | HTML; 45 | $this->sidebarContent = << 47 |
  • 48 |

    49 | Level: Intermediate 50 |

    51 |

    52 | Total: 1 hr 15 min 53 |

    54 |
  • 55 |
  • 56 |

    57 | Prep: 1 hr 58 |

    59 |

    60 | Cook: 15 min 61 |

    62 |
  • 63 |
  • 64 |

    65 | Yield: 16 enchiladas, 8 servings 66 |

    67 |

    68 | Nutrition Information 69 |

    70 |
  • 71 | 72 | HTML; 73 | } 74 | 75 | /** @test */ 76 | public function can_output_read_time() 77 | { 78 | $result = (new ReadTime($this->mainContent))->get(); 79 | $this->assertSame($result, '1 minute read'); 80 | } 81 | 82 | /** @test */ 83 | public function can_accept_array_of_content() 84 | { 85 | $result = (new ReadTime([$this->mainContent, $this->sidebarContent]))->get(); 86 | $this->assertSame($result, '1 minute read'); 87 | } 88 | 89 | /** @test */ 90 | public function can_change_wpm() 91 | { 92 | $result = (new ReadTime($this->mainContent))->wpm(150)->toArray(); 93 | $this->assertSame($result['words_per_minute'], 150); 94 | } 95 | 96 | /** @test */ 97 | public function can_set_time_only() 98 | { 99 | $result = (new ReadTime($this->mainContent))->timeOnly(true)->toArray(); 100 | $this->assertTrue($result['time_only']); 101 | } 102 | 103 | /** @test */ 104 | public function can_allow_seconds() 105 | { 106 | $result = (new ReadTime($this->mainContent))->omitSeconds(false)->toArray(); 107 | $this->assertFalse($result['omit_seconds']); 108 | } 109 | 110 | /** @test */ 111 | public function can_change_translation() 112 | { 113 | $spanish = [ 114 | 'min' => 'min', 115 | 'minute' => 'minuto', 116 | 'sec' => 'seg', 117 | 'second' => 'segundo', 118 | 'read' => 'leer' 119 | ]; 120 | $result = (new ReadTime($this->mainContent))->setTranslation($spanish)->getTranslation('read'); 121 | $this->assertSame($result, 'leer'); 122 | } 123 | 124 | /** @test */ 125 | public function can_get_translation_array() 126 | { 127 | $result = (new ReadTime($this->mainContent))->getTranslation(); 128 | $this->assertIsArray($result); 129 | } 130 | 131 | /** @test */ 132 | public function can_get_translation_key() 133 | { 134 | $result = (new ReadTime($this->mainContent))->getTranslation('minute'); 135 | $this->assertSame($result, 'minute'); 136 | } 137 | 138 | /** @test */ 139 | public function can_read_right_to_left() 140 | { 141 | $result = (new ReadTime($this->mainContent))->omitSeconds(false)->rtl()->get(); 142 | $this->assertSame($result, 'read second 10 minute 1'); 143 | } 144 | 145 | /** @test */ 146 | public function can_output_array() 147 | { 148 | $result = (new ReadTime($this->mainContent))->toArray(); 149 | $this->assertIsArray($result); 150 | } 151 | 152 | /** @test */ 153 | public function can_output_json() 154 | { 155 | $result = (new ReadTime($this->mainContent))->toJson(); 156 | $this->assertJson($result); 157 | } 158 | 159 | /** @test */ 160 | public function can_invoke_class_for_read_time() 161 | { 162 | $result = new ReadTime($this->mainContent); 163 | $this->assertIsString($result()); 164 | } 165 | 166 | /** @test */ 167 | public function can_cast_as_string_for_read_time() 168 | { 169 | $result = new ReadTime($this->mainContent); 170 | $this->assertIsString((string)$result); 171 | } 172 | 173 | /** @test */ 174 | public function can_handle_large_content() 175 | { 176 | $result = (new ReadTime(str_repeat($this->mainContent, 50)))->get(); 177 | $this->assertSame($result, '58 minute read'); 178 | } 179 | } 180 | --------------------------------------------------------------------------------