├── .README └── README.md ├── .babelrc ├── .editorconfig ├── .eslintrc ├── .flowconfig ├── .github └── FUNDING.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── package.json ├── src ├── Logger.js ├── calculateSpecificity.js ├── createFormats.js ├── createMovingChunks.js ├── dictionary.json ├── extractDate.js ├── extractRelativeDate.js ├── index.js ├── normalizeInput.js └── types.js └── test ├── .eslintrc ├── extract-date ├── calculateSpecificity.js ├── createFormats.js ├── createMovingChunks.js ├── extractDate │ ├── configuration.js │ ├── edge-cases.js │ ├── fixtures.js │ ├── general-formats.js │ ├── implied-year.js │ ├── localised.js │ ├── multiple-dates.js │ └── relative-dates.js ├── extractRelativeDate.js └── normalizeInput.js └── fixtures ├── README.md └── dates.json /.README/README.md: -------------------------------------------------------------------------------- 1 | # extract-date 📅 2 | 3 | [![Travis build status](http://img.shields.io/travis/gajus/extract-date/master.svg?style=flat-square)](https://travis-ci.org/gajus/extract-date) 4 | [![Coveralls](https://img.shields.io/coveralls/gajus/extract-date.svg?style=flat-square)](https://coveralls.io/github/gajus/extract-date) 5 | [![NPM version](http://img.shields.io/npm/v/extract-date.svg?style=flat-square)](https://www.npmjs.org/package/extract-date) 6 | [![Canonical Code Style](https://img.shields.io/badge/code%20style-canonical-blue.svg?style=flat-square)](https://github.com/gajus/canonical) 7 | [![Twitter Follow](https://img.shields.io/twitter/follow/kuizinas.svg?style=social&label=Follow)](https://twitter.com/kuizinas) 8 | 9 | Extracts date from an arbitrary text input. 10 | 11 | {"gitdown": "contents"} 12 | 13 | ## Features 14 | 15 | * Deterministic and unambiguous date parsing (input must include year; see [Date resolution without year](#date-resolution-without-year)) 16 | * No date format configuration. 17 | * Recognises relative dates (yesterday, today, tomorrow). 18 | * Recognises weekdays (Monday, Tuesday, etc.). 19 | * Supports timezones (for relative date resolution) and locales. 20 | 21 | ## Motivation 22 | 23 | I am creating a large scale data aggregation platform (https://applaudience.com/). I have observed that the date-matching patterns and site specific date validation logic is repeating and could be abstracted into a universal function as long as minimum information about the expected pattern is provided (such as the `direction` configuration). My motivation for creating such abstraction is to reduce the amount of repetitive logic that we use to extract dates from multiple sources. 24 | 25 | ## Use case 26 | 27 | The intended use case is extracting date of future events from blobs of text that may contain auxiliary information, e.g. 'Event at 14:00 2019-01-01 (2D)'. 28 | 29 | The emphasis on the _future_ events is because resolving dates such 'today' (relative dates) and 'Wednesday' (weekday dates) requires knowing the offset date. If your input sources refer predominantly to future events, then the ambiguity can be resolved using the present date. 30 | 31 | ## Usage 32 | 33 | ```js 34 | import extractDate from 'extract-date'; 35 | 36 | extractDate('extracts date from anywhere within the input 2000-01-02'); 37 | // [{date: '2000-01-02'}] 38 | 39 | extractDate('extracts multiple dates located anywhere within the input: 2000-01-02, 2000-01-03'); 40 | // [{date: '2000-01-02'}, {date: '2000-01-03'}] 41 | 42 | extractDate('ignores ambiguous dates 02/01/2000'); 43 | // [] 44 | 45 | extractDate('uses `direction` to resolve ambiguous dates 02/01/2000', {direction: 'DMY'}); 46 | // [{date: '2000-01-02'}] 47 | 48 | extractDate('uses `timezone` to resolve relative dates such as today or tomorrow', {timezone: 'Europe/London'}); 49 | // [{date: '2000-01-02'}, {date: '2000-01-03'}] (assuming that today is 2000-01-02) 50 | 51 | extractDate('extracts dates using locales May 1, 2017', {locale: 'en'}); 52 | // [{date: '2015-05-01'}] 53 | 54 | ``` 55 | 56 | ### Configuration 57 | 58 | |Name|Description|Default| 59 | |---|---|---| 60 | |`direction`|Token identifying the order of numeric date attributes within the string. Possible values: DM, DMY, DYM, MD, YDM, YMD. Used to resolve ambiguous dates, e.g. DD/MM/YYYY and MM/DD/YYYY.|N/A| 61 | |`locale`|Required when date includes localized names (e.g. month names)|N/A| 62 | |`maximumAge`|See [Date resolution without year](#date-resolution-without-year).|`Infinity`| 63 | |`minimumAge`|See [Date resolution without year](#date-resolution-without-year).|`Infinity`| 64 | |`timezone`|[TZ database name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). Used to resolve relative dates ("Today", "Tomorrow").|N/A| 65 | 66 | ## Resolution of ambiguous dates 67 | 68 | ### Date resolution without year 69 | 70 | When year is not part of the input (e.g. March 2nd), then `minimumAge` and `maximumAge` configuration determines the year value. 71 | 72 | * If the difference between the current month and the parsed month is greater or equal to `minimumAge`, then the year value is equal to the current year +1. 73 | * If the difference between the current month and the parsed month is lower or equal to `maximumAge`, then the year value is equal to the current year -1. 74 | * If the difference is within those two ranges, then the current year value is used. 75 | 76 | Example: 77 | 78 | * If the current date is 2000-12-01 and the parsed date is 10-01, then the month difference is -2. 79 | * If `minimumAge` is 2, then the final date is 2001-10-01. 80 | * If `minimumAge` is 3, then the final date is 2000-10-01. 81 | 82 | * If the current date is 2000-01-01 and the input date is 10-01, then the month difference is 9. 83 | * If `maximumAge` is 10, then the final date is 2000-10-01. 84 | * If `maximumAge` is 9, then the final date is 1999-10-01. 85 | 86 | Note: `minimumAge` comparison is done using absolute difference value. 87 | 88 | ## Implementation 89 | 90 | Note: This section of the documentation is included for contributors. 91 | 92 | * `extract-date` includes a collection of formats ([`./src/createFormats.js`](./src/createFormats.js)). 93 | * Individual formats define their expectations (see [Format specification](#format-specification)). 94 | * Formats are attempted in the order of their specificity, i.e. "YYYY-MM-DD" is attempted before "MM-DD". 95 | * Formats are attempted against a tokenised version of the input (see [Input tokenisation](#input-tokenisation)). 96 | * Matching date format advances further search past the matching date string. 97 | 98 | ### Input tokenisation 99 | 100 | * Individual formats define how many words make up the date. 101 | * `extract-date` splits input string into a collection of slices pairing words into phrases of the required length. 102 | * Format is attempted against each resulting phrase. 103 | 104 | Example: 105 | 106 | Given input "foo bar baz qux" and format: 107 | 108 | ```js 109 | { 110 | direction: 'YMD', 111 | localised: false, 112 | dateFnsFormat: 'YYYY MM.DD', 113 | wordCount: 2, 114 | yearIsExplicit: true 115 | } 116 | 117 | ``` 118 | 119 | Input is broken down into: 120 | 121 | * "foo bar" 122 | * "bar baz" 123 | * "baz qux" 124 | 125 | collection and the format is attempted against each phrase until a match is found. 126 | 127 | ### Format specification 128 | 129 | |Field|Description| 130 | |---|---| 131 | |`direction`|Identifies the order of numeric date attributes within the string. Possible values: DMY, DYM, YDM, YMD. Used to resolve ambiguous dates, e.g. DD/MM/YYYY and MM/DD/YYYY.| 132 | |`localised`|Identifies if the date is localised, i.e. includes names of the week day or month. A format that is localised is used only when `locale` configuration is provided.| 133 | |`dateFnsFormat`|Identifies [`date-fns`](https://www.npmjs.org/package/date-fns) format used to attempt date extraction.| 134 | |`wordCount`|Identifies how many words make up the date format.| 135 | |`yearIsExplicit`|Identifies whether the date format includes year.| 136 | 137 | Example formats: 138 | 139 | ```js 140 | { 141 | direction: 'YMD', 142 | localised: false, 143 | dateFnsFormat: 'YYYY.MM.DD', 144 | wordCount: 1, 145 | yearIsExplicit: true 146 | }, 147 | { 148 | direction: 'DD MMMM', 149 | localised: true, 150 | dateFnsFormat: 'DD MMMM', 151 | wordCount: 2, 152 | yearIsExplicit: false 153 | }, 154 | 155 | ``` 156 | 157 | ## Related projects 158 | 159 | * [`extract-price`](https://github.com/gajus/extract-price) – Extracts price from an arbitrary text input. 160 | * [`extract-time`](https://github.com/gajus/extract-time) – Extracts time from an arbitrary text input. 161 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "test": { 4 | "plugins": [ 5 | "istanbul" 6 | ] 7 | } 8 | }, 9 | "plugins": [ 10 | "@babel/transform-flow-strip-types" 11 | ], 12 | "presets": [ 13 | [ 14 | "@babel/env", 15 | { 16 | "targets": { 17 | "node": "10" 18 | } 19 | } 20 | ] 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "canonical", 4 | "canonical/flowtype" 5 | ], 6 | "root": true 7 | } 8 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/.*/test/.* 3 | /dist/.* 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: gajus 2 | patreon: gajus 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | dist 3 | node_modules 4 | *.log 5 | .* 6 | !.babelrc 7 | !.editorconfig 8 | !.eslintignore 9 | !.eslintrc 10 | !.flowconfig 11 | !.gitignore 12 | !.npmignore 13 | !.README 14 | !.travis.yml 15 | /package-lock.json 16 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | coverage 3 | .* 4 | *.log 5 | !.flowconfig 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | - 10 5 | script: 6 | - npm run lint 7 | - npm run test 8 | - nyc --silent npm run test 9 | - nyc report --reporter=text-lcov | coveralls 10 | - nyc check-coverage --lines 60 11 | after_success: 12 | - NODE_ENV=production npm run build 13 | - semantic-release 14 | notifications: 15 | email: false 16 | sudo: false 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Gajus Kuizinas (http://gajus.com/) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the Gajus Kuizinas (http://gajus.com/) nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL ANUARY BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # extract-date 📅 3 | 4 | [![Travis build status](http://img.shields.io/travis/gajus/extract-date/master.svg?style=flat-square)](https://travis-ci.org/gajus/extract-date) 5 | [![Coveralls](https://img.shields.io/coveralls/gajus/extract-date.svg?style=flat-square)](https://coveralls.io/github/gajus/extract-date) 6 | [![NPM version](http://img.shields.io/npm/v/extract-date.svg?style=flat-square)](https://www.npmjs.org/package/extract-date) 7 | [![Canonical Code Style](https://img.shields.io/badge/code%20style-canonical-blue.svg?style=flat-square)](https://github.com/gajus/canonical) 8 | [![Twitter Follow](https://img.shields.io/twitter/follow/kuizinas.svg?style=social&label=Follow)](https://twitter.com/kuizinas) 9 | 10 | Extracts date from an arbitrary text input. 11 | 12 | * [extract-date 📅](#extract-date) 13 | * [Features](#extract-date-features) 14 | * [Motivation](#extract-date-motivation) 15 | * [Use case](#extract-date-use-case) 16 | * [Usage](#extract-date-usage) 17 | * [Configuration](#extract-date-usage-configuration) 18 | * [Resolution of ambiguous dates](#extract-date-resolution-of-ambiguous-dates) 19 | * [Date resolution without year](#extract-date-resolution-of-ambiguous-dates-date-resolution-without-year) 20 | * [Implementation](#extract-date-implementation) 21 | * [Input tokenisation](#extract-date-implementation-input-tokenisation) 22 | * [Format specification](#extract-date-implementation-format-specification) 23 | * [Related projects](#extract-date-related-projects) 24 | 25 | 26 | 27 | ## Features 28 | 29 | * Deterministic and unambiguous date parsing (input must include year; see [Date resolution without year](#date-resolution-without-year)) 30 | * No date format configuration. 31 | * Recognises relative dates (yesterday, today, tomorrow). 32 | * Recognises weekdays (Monday, Tuesday, etc.). 33 | * Supports timezones (for relative date resolution) and locales. 34 | 35 | 36 | ## Motivation 37 | 38 | I am creating a large scale data aggregation platform (https://applaudience.com/). I have observed that the date-matching patterns and site specific date validation logic is repeating and could be abstracted into a universal function as long as minimum information about the expected pattern is provided (such as the `direction` configuration). My motivation for creating such abstraction is to reduce the amount of repetitive logic that we use to extract dates from multiple sources. 39 | 40 | 41 | ## Use case 42 | 43 | The intended use case is extracting date of future events from blobs of text that may contain auxiliary information, e.g. 'Event at 14:00 2019-01-01 (2D)'. 44 | 45 | The emphasis on the _future_ events is because resolving dates such 'today' (relative dates) and 'Wednesday' (weekday dates) requires knowing the offset date. If your input sources refer predominantly to future events, then the ambiguity can be resolved using the present date. 46 | 47 | 48 | ## Usage 49 | 50 | ```js 51 | import extractDate from 'extract-date'; 52 | 53 | extractDate('extracts date from anywhere within the input 2000-01-02'); 54 | // [{date: '2000-01-02'}] 55 | 56 | extractDate('extracts multiple dates located anywhere within the input: 2000-01-02, 2000-01-03'); 57 | // [{date: '2000-01-02'}, {date: '2000-01-03'}] 58 | 59 | extractDate('ignores ambiguous dates 02/01/2000'); 60 | // [] 61 | 62 | extractDate('uses `direction` to resolve ambiguous dates 02/01/2000', {direction: 'DMY'}); 63 | // [{date: '2000-01-02'}] 64 | 65 | extractDate('uses `timezone` to resolve relative dates such as today or tomorrow', {timezone: 'Europe/London'}); 66 | // [{date: '2000-01-02'}, {date: '2000-01-03'}] (assuming that today is 2000-01-02) 67 | 68 | extractDate('extracts dates using locales May 1, 2017', {locale: 'en'}); 69 | // [{date: '2015-05-01'}] 70 | 71 | ``` 72 | 73 | 74 | ### Configuration 75 | 76 | |Name|Description|Default| 77 | |---|---|---| 78 | |`direction`|Token identifying the order of numeric date attributes within the string. Possible values: DM, DMY, DYM, MD, YDM, YMD. Used to resolve ambiguous dates, e.g. DD/MM/YYYY and MM/DD/YYYY.|N/A| 79 | |`locale`|Required when date includes localized names (e.g. month names)|N/A| 80 | |`maximumAge`|See [Date resolution without year](#date-resolution-without-year).|`Infinity`| 81 | |`minimumAge`|See [Date resolution without year](#date-resolution-without-year).|`Infinity`| 82 | |`timezone`|[TZ database name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). Used to resolve relative dates ("Today", "Tomorrow").|N/A| 83 | 84 | 85 | ## Resolution of ambiguous dates 86 | 87 | 88 | ### Date resolution without year 89 | 90 | When year is not part of the input (e.g. March 2nd), then `minimumAge` and `maximumAge` configuration determines the year value. 91 | 92 | * If the difference between the current month and the parsed month is greater or equal to `minimumAge`, then the year value is equal to the current year +1. 93 | * If the difference between the current month and the parsed month is lower or equal to `maximumAge`, then the year value is equal to the current year -1. 94 | * If the difference is within those two ranges, then the current year value is used. 95 | 96 | Example: 97 | 98 | * If the current date is 2000-12-01 and the parsed date is 10-01, then the month difference is -2. 99 | * If `minimumAge` is 2, then the final date is 2001-10-01. 100 | * If `minimumAge` is 3, then the final date is 2000-10-01. 101 | 102 | * If the current date is 2000-01-01 and the input date is 10-01, then the month difference is 9. 103 | * If `maximumAge` is 10, then the final date is 2000-10-01. 104 | * If `maximumAge` is 9, then the final date is 1999-10-01. 105 | 106 | Note: `minimumAge` comparison is done using absolute difference value. 107 | 108 | 109 | ## Implementation 110 | 111 | Note: This section of the documentation is included for contributors. 112 | 113 | * `extract-date` includes a collection of formats ([`./src/createFormats.js`](./src/createFormats.js)). 114 | * Individual formats define their expectations (see [Format specification](#format-specification)). 115 | * Formats are attempted in the order of their specificity, i.e. "YYYY-MM-DD" is attempted before "MM-DD". 116 | * Formats are attempted against a tokenised version of the input (see [Input tokenisation](#input-tokenisation)). 117 | * Matching date format advances further search past the matching date string. 118 | 119 | 120 | ### Input tokenisation 121 | 122 | * Individual formats define how many words make up the date. 123 | * `extract-date` splits input string into a collection of slices pairing words into phrases of the required length. 124 | * Format is attempted against each resulting phrase. 125 | 126 | Example: 127 | 128 | Given input "foo bar baz qux" and format: 129 | 130 | ```js 131 | { 132 | direction: 'YMD', 133 | localised: false, 134 | dateFnsFormat: 'YYYY MM.DD', 135 | wordCount: 2, 136 | yearIsExplicit: true 137 | } 138 | 139 | ``` 140 | 141 | Input is broken down into: 142 | 143 | * "foo bar" 144 | * "bar baz" 145 | * "baz qux" 146 | 147 | collection and the format is attempted against each phrase until a match is found. 148 | 149 | 150 | ### Format specification 151 | 152 | |Field|Description| 153 | |---|---| 154 | |`direction`|Identifies the order of numeric date attributes within the string. Possible values: DMY, DYM, YDM, YMD. Used to resolve ambiguous dates, e.g. DD/MM/YYYY and MM/DD/YYYY.| 155 | |`localised`|Identifies if the date is localised, i.e. includes names of the week day or month. A format that is localised is used only when `locale` configuration is provided.| 156 | |`dateFnsFormat`|Identifies [`date-fns`](https://www.npmjs.org/package/date-fns) format used to attempt date extraction.| 157 | |`wordCount`|Identifies how many words make up the date format.| 158 | |`yearIsExplicit`|Identifies whether the date format includes year.| 159 | 160 | Example formats: 161 | 162 | ```js 163 | { 164 | direction: 'YMD', 165 | localised: false, 166 | dateFnsFormat: 'YYYY.MM.DD', 167 | wordCount: 1, 168 | yearIsExplicit: true 169 | }, 170 | { 171 | direction: 'DD MMMM', 172 | localised: true, 173 | dateFnsFormat: 'DD MMMM', 174 | wordCount: 2, 175 | yearIsExplicit: false 176 | }, 177 | 178 | ``` 179 | 180 | 181 | ## Related projects 182 | 183 | * [`extract-price`](https://github.com/gajus/extract-price) – Extracts price from an arbitrary text input. 184 | * [`extract-time`](https://github.com/gajus/extract-time) – Extracts time from an arbitrary text input. 185 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "email": "gajus@gajus.com", 4 | "name": "Gajus Kuizinas", 5 | "url": "http://gajus.com" 6 | }, 7 | "ava": { 8 | "babel": { 9 | "compileAsTests": [ 10 | "test/helpers/**/*" 11 | ] 12 | }, 13 | "files": [ 14 | "test/extract-date/**/*" 15 | ], 16 | "require": [ 17 | "@babel/register" 18 | ] 19 | }, 20 | "dependencies": { 21 | "cartesian": "^1.0.1", 22 | "date-fns": "^2.9.0", 23 | "moment": "^2.24.0", 24 | "moment-timezone": "^0.5.27", 25 | "relative-date-names": "^1.0.4", 26 | "roarr": "^2.15.2" 27 | }, 28 | "description": "Extracts date from an arbitrary text input.", 29 | "devDependencies": { 30 | "@ava/babel": "^1.0.0", 31 | "@babel/cli": "^7.8.4", 32 | "@babel/core": "^7.8.4", 33 | "@babel/node": "^7.8.4", 34 | "@babel/plugin-transform-flow-strip-types": "^7.8.3", 35 | "@babel/preset-env": "^7.8.4", 36 | "@babel/register": "^7.8.3", 37 | "ava": "^3.1.0", 38 | "babel-plugin-istanbul": "^6.0.0", 39 | "coveralls": "^3.0.9", 40 | "eslint": "^6.8.0", 41 | "eslint-config-canonical": "^18.1.0", 42 | "flow-bin": "^0.117.0", 43 | "flow-copy-source": "^2.0.9", 44 | "gitdown": "^3.1.2", 45 | "husky": "^4.2.1", 46 | "nyc": "^15.0.0", 47 | "semantic-release": "^17.0.2", 48 | "sinon": "^8.1.1" 49 | }, 50 | "engines": { 51 | "node": ">6" 52 | }, 53 | "husky": { 54 | "hooks": { 55 | "pre-commit": "npm run lint && npm run test && npm run build", 56 | "pre-push": "gitdown ./.README/README.md --output-file ./README.md --check" 57 | } 58 | }, 59 | "keywords": [ 60 | "date", 61 | "extract", 62 | "moment", 63 | "parse" 64 | ], 65 | "license": "BSD-3-Clause", 66 | "main": "./dist/index.js", 67 | "name": "extract-date", 68 | "nyc": { 69 | "include": [ 70 | "src/**/*.js" 71 | ], 72 | "instrument": false, 73 | "reporter": [ 74 | "text-lcov" 75 | ], 76 | "require": [ 77 | "@babel/register" 78 | ], 79 | "sourceMap": false 80 | }, 81 | "repository": { 82 | "type": "git", 83 | "url": "https://github.com/gajus/extract-date" 84 | }, 85 | "scripts": { 86 | "build": "rm -fr ./dist && NODE_ENV=production babel ./src --out-dir ./dist --copy-files --source-maps && flow-copy-source src dist", 87 | "create-readme": "gitdown ./.README/README.md --output-file ./README.md", 88 | "lint": "eslint ./src ./test && flow", 89 | "test": "NODE_ENV=test ava --verbose --serial" 90 | }, 91 | "version": "2.0.0" 92 | } 93 | -------------------------------------------------------------------------------- /src/Logger.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import Roarr from 'roarr'; 4 | 5 | export default Roarr.child({ 6 | package: 'extract-date', 7 | }); 8 | -------------------------------------------------------------------------------- /src/calculateSpecificity.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default (dateFnsFormat: string): number => { 4 | let specificity = 0; 5 | 6 | if (dateFnsFormat.includes('yyyy')) { 7 | specificity += 40; 8 | } else if (dateFnsFormat.includes('yy')) { 9 | specificity += 20; 10 | } 11 | 12 | if (dateFnsFormat.includes('M')) { 13 | specificity += 20; 14 | } 15 | 16 | if (/d/.test(dateFnsFormat)) { 17 | specificity += 20; 18 | } 19 | 20 | return specificity + dateFnsFormat.length; 21 | }; 22 | -------------------------------------------------------------------------------- /src/createFormats.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import cartesian from 'cartesian'; 4 | import calculateSpecificity from './calculateSpecificity'; 5 | 6 | export default () => { 7 | // The reason `yearFirstDashSeparator` and `yearFirstSlashSeparator` formats do not have direction is because 8 | // there are no known regions that use yyyy-dd-MM format. 9 | // https://en.wikipedia.org/wiki/Date_format_by_country 10 | const yearFirstDashSeparator = [ 11 | { 12 | dateFnsFormat: 'yyyy-MM-dd', 13 | }, 14 | { 15 | dateFnsFormat: 'yyyy-M-d', 16 | }, 17 | ]; 18 | 19 | const yearFirstSlashSeparator = [ 20 | { 21 | dateFnsFormat: 'yyyy/MM/dd', 22 | }, 23 | { 24 | dateFnsFormat: 'yyyy/M/d', 25 | }, 26 | ]; 27 | 28 | const yearFirstDotSeparator = [ 29 | { 30 | dateFnsFormat: 'yyyy.MM.dd', 31 | direction: 'YMD', 32 | }, 33 | { 34 | dateFnsFormat: 'yyyy.M.d', 35 | direction: 'YMD', 36 | }, 37 | { 38 | dateFnsFormat: 'yyyy.dd.MM', 39 | direction: 'YDM', 40 | }, 41 | { 42 | dateFnsFormat: 'yyyy.d.M', 43 | direction: 'YDM', 44 | }, 45 | ]; 46 | 47 | const yearLastDashSeparator = [ 48 | { 49 | dateFnsFormat: 'dd-MM-yyyy', 50 | direction: 'DMY', 51 | }, 52 | { 53 | dateFnsFormat: 'd-M-yyyy', 54 | direction: 'DMY', 55 | }, 56 | { 57 | dateFnsFormat: 'MM-dd-yyyy', 58 | direction: 'MDY', 59 | }, 60 | { 61 | dateFnsFormat: 'M-d-yyyy', 62 | direction: 'MDY', 63 | }, 64 | ]; 65 | 66 | const yearLastDotSeparator = [ 67 | { 68 | dateFnsFormat: 'dd.MM.yyyy', 69 | direction: 'DMY', 70 | }, 71 | { 72 | dateFnsFormat: 'd.M.yyyy', 73 | direction: 'DMY', 74 | }, 75 | { 76 | dateFnsFormat: 'MM.dd.yyyy', 77 | direction: 'MDY', 78 | }, 79 | { 80 | dateFnsFormat: 'M.d.yyyy', 81 | direction: 'MDY', 82 | }, 83 | { 84 | dateFnsFormat: 'dd.MM.yy', 85 | direction: 'DMY', 86 | }, 87 | { 88 | dateFnsFormat: 'd.M.yy', 89 | direction: 'DMY', 90 | }, 91 | ]; 92 | 93 | const yearLastSlashSeparator = [ 94 | { 95 | dateFnsFormat: 'dd/MM/yyyy', 96 | direction: 'DMY', 97 | }, 98 | { 99 | dateFnsFormat: 'd/M/yyyy', 100 | direction: 'DMY', 101 | }, 102 | { 103 | dateFnsFormat: 'MM/dd/yyyy', 104 | direction: 'MDY', 105 | }, 106 | { 107 | dateFnsFormat: 'M/d/yyyy', 108 | direction: 'MDY', 109 | }, 110 | { 111 | dateFnsFormat: 'MM/dd/yy', 112 | direction: 'MDY', 113 | }, 114 | { 115 | dateFnsFormat: 'dd/MM/yy', 116 | direction: 'DMY', 117 | }, 118 | { 119 | dateFnsFormat: 'd/M/yy', 120 | direction: 'DMY', 121 | }, 122 | { 123 | dateFnsFormat: 'M/d/yy', 124 | direction: 'MDY', 125 | }, 126 | ]; 127 | 128 | const localised = [ 129 | { 130 | dateFnsFormat: 'MMMM d yyyy', 131 | }, 132 | { 133 | dateFnsFormat: 'MMMM do yyyy', 134 | }, 135 | ...cartesian([ 136 | [ 137 | 'do', 138 | 'd', 139 | ], 140 | [ 141 | 'MMMM', 142 | 'MMM', 143 | ], 144 | [ 145 | 'yyyy', 146 | ], 147 | ]) 148 | .map((combination) => { 149 | return { 150 | dateFnsFormat: combination.join(' '), 151 | }; 152 | }), 153 | ...cartesian([ 154 | [ 155 | 'MMMM', 156 | 'MMM', 157 | ], 158 | [ 159 | 'yyyy', 160 | ], 161 | [ 162 | 'do', 163 | 'd', 164 | ], 165 | ]) 166 | .map((combination) => { 167 | return { 168 | dateFnsFormat: combination.join(' '), 169 | }; 170 | }), 171 | { 172 | dateFnsFormat: 'MMMM yyyy EEE do', 173 | }, 174 | { 175 | dateFnsFormat: 'MMMM yyyy EEE d', 176 | }, 177 | ]; 178 | 179 | const impliedYearLocalised = [ 180 | ...cartesian([ 181 | [ 182 | 'EEEE', 183 | 'EEE', 184 | ], 185 | [ 186 | 'MMMM', 187 | 'MMM', 188 | ], 189 | [ 190 | 'dd', 191 | 'do', 192 | 'd', 193 | ], 194 | ]) 195 | .map((combination) => { 196 | return { 197 | dateFnsFormat: combination.join(' '), 198 | }; 199 | }), 200 | ...cartesian([ 201 | [ 202 | 'EEEE', 203 | 'EEE', 204 | ], 205 | [ 206 | 'dd', 207 | 'do', 208 | 'd', 209 | ], 210 | [ 211 | 'MMMM', 212 | 'MMM', 213 | ], 214 | ]) 215 | .map((combination) => { 216 | return { 217 | dateFnsFormat: combination.join(' '), 218 | }; 219 | }), 220 | ...cartesian([ 221 | [ 222 | 'MMMM', 223 | 'MMM', 224 | ], 225 | [ 226 | 'dd', 227 | 'do', 228 | 'd', 229 | ], 230 | ]) 231 | .map((combination) => { 232 | return { 233 | dateFnsFormat: combination.join(' '), 234 | }; 235 | }), 236 | ...cartesian([ 237 | [ 238 | 'dd', 239 | 'do', 240 | 'd', 241 | ], 242 | [ 243 | 'MMMM', 244 | 'MMM', 245 | ], 246 | ]) 247 | .map((combination) => { 248 | return { 249 | dateFnsFormat: combination.join(' '), 250 | }; 251 | }), 252 | ]; 253 | 254 | const impliedYear = [ 255 | ...cartesian([ 256 | [ 257 | 'dd', 258 | 'd', 259 | ], 260 | [ 261 | '/', 262 | '-', 263 | '.', 264 | ], 265 | [ 266 | 'MM', 267 | 'M', 268 | ], 269 | ]) 270 | .map((combination) => { 271 | return { 272 | dateFnsFormat: combination.join(''), 273 | direction: 'DM', 274 | }; 275 | }), 276 | ...cartesian([ 277 | [ 278 | 'MM', 279 | 'M', 280 | ], 281 | [ 282 | '/', 283 | '-', 284 | '.', 285 | ], 286 | [ 287 | 'dd', 288 | 'd', 289 | ], 290 | ]) 291 | .map((combination) => { 292 | return { 293 | dateFnsFormat: combination.join(''), 294 | direction: 'MD', 295 | }; 296 | }), 297 | ]; 298 | 299 | const relative = [ 300 | { 301 | dateFnsFormat: 'R', 302 | test: false, 303 | }, 304 | { 305 | dateFnsFormat: 'EEEE', 306 | }, 307 | { 308 | dateFnsFormat: 'EEE', 309 | }, 310 | ]; 311 | 312 | return [ 313 | { 314 | dateFnsFormat: 'yyyyMMdd', 315 | }, 316 | ...yearFirstDashSeparator, 317 | ...yearFirstDotSeparator, 318 | ...yearFirstSlashSeparator, 319 | ...yearLastDashSeparator, 320 | ...yearLastDotSeparator, 321 | ...yearLastSlashSeparator, 322 | ...localised, 323 | ...impliedYearLocalised, 324 | ...impliedYear, 325 | ...relative, 326 | ] 327 | .map((format) => { 328 | return { 329 | localised: /eee|mmm/i.test(format.dateFnsFormat), 330 | specificity: calculateSpecificity(format.dateFnsFormat), 331 | wordCount: format.dateFnsFormat.replace(/[^ ]/g, '').length + 1, 332 | yearIsExplicit: format.dateFnsFormat.includes('yyyy'), 333 | ...format, 334 | }; 335 | }) 336 | .sort((a, b) => { 337 | if (a.wordCount !== b.wordCount) { 338 | return b.wordCount - a.wordCount; 339 | } 340 | 341 | if (b.specificity === a.specificity) { 342 | return a.dateFnsFormat.localeCompare(b.dateFnsFormat); 343 | } 344 | 345 | return b.specificity - a.specificity; 346 | }); 347 | }; 348 | -------------------------------------------------------------------------------- /src/createMovingChunks.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default (haystack: $ReadOnlyArray, sliceLength: number): $ReadOnlyArray<$ReadOnlyArray> => { 4 | const slices = []; 5 | 6 | let index = 0; 7 | 8 | while (index < haystack.length) { 9 | const result = haystack.slice(index, index + sliceLength); 10 | 11 | if (result.length === sliceLength) { 12 | slices.push(result); 13 | } 14 | 15 | index++; 16 | } 17 | 18 | return slices; 19 | }; 20 | -------------------------------------------------------------------------------- /src/dictionary.json: -------------------------------------------------------------------------------- 1 | { 2 | "af": { 3 | "0": "vandag", 4 | "1": "môre", 5 | "-1": "gister" 6 | }, 7 | "af_na": { 8 | "0": "vandag", 9 | "1": "môre", 10 | "-1": "gister" 11 | }, 12 | "af_za": { 13 | "0": "vandag", 14 | "1": "môre", 15 | "-1": "gister" 16 | }, 17 | "agq": { 18 | "0": "nɛ", 19 | "1": "tsʉtsʉ", 20 | "-1": "ā zūɛɛ" 21 | }, 22 | "agq_cm": { 23 | "0": "nɛ", 24 | "1": "tsʉtsʉ", 25 | "-1": "ā zūɛɛ" 26 | }, 27 | "ak": { 28 | "0": "Ndɛ", 29 | "1": "Ɔkyena", 30 | "-1": "Ndeda" 31 | }, 32 | "ak_gh": { 33 | "0": "Ndɛ", 34 | "1": "Ɔkyena", 35 | "-1": "Ndeda" 36 | }, 37 | "am": { 38 | "0": "ዛሬ", 39 | "1": "ነገ", 40 | "-1": "ትናንት" 41 | }, 42 | "am_et": { 43 | "0": "ዛሬ", 44 | "1": "ነገ", 45 | "-1": "ትናንት" 46 | }, 47 | "ar": { 48 | "0": "اليوم", 49 | "1": "غدًا", 50 | "-1": "أمس" 51 | }, 52 | "ar_001": { 53 | "0": "اليوم", 54 | "1": "غدًا", 55 | "-1": "أمس" 56 | }, 57 | "ar_ae": { 58 | "0": "اليوم", 59 | "1": "غدًا", 60 | "-1": "أمس" 61 | }, 62 | "ar_bh": { 63 | "0": "اليوم", 64 | "1": "غدًا", 65 | "-1": "أمس" 66 | }, 67 | "ar_dj": { 68 | "0": "اليوم", 69 | "1": "غدًا", 70 | "-1": "أمس" 71 | }, 72 | "ar_dz": { 73 | "0": "اليوم", 74 | "1": "غدًا", 75 | "-1": "أمس" 76 | }, 77 | "ar_eg": { 78 | "0": "اليوم", 79 | "1": "غدًا", 80 | "-1": "أمس" 81 | }, 82 | "ar_eh": { 83 | "0": "اليوم", 84 | "1": "غدًا", 85 | "-1": "أمس" 86 | }, 87 | "ar_er": { 88 | "0": "اليوم", 89 | "1": "غدًا", 90 | "-1": "أمس" 91 | }, 92 | "ar_il": { 93 | "0": "اليوم", 94 | "1": "غدًا", 95 | "-1": "أمس" 96 | }, 97 | "ar_iq": { 98 | "0": "اليوم", 99 | "1": "غدًا", 100 | "-1": "أمس" 101 | }, 102 | "ar_jo": { 103 | "0": "اليوم", 104 | "1": "غدًا", 105 | "-1": "أمس" 106 | }, 107 | "ar_km": { 108 | "0": "اليوم", 109 | "1": "غدًا", 110 | "-1": "أمس" 111 | }, 112 | "ar_kw": { 113 | "0": "اليوم", 114 | "1": "غدًا", 115 | "-1": "أمس" 116 | }, 117 | "ar_lb": { 118 | "0": "اليوم", 119 | "1": "غدًا", 120 | "-1": "أمس" 121 | }, 122 | "ar_ly": { 123 | "0": "اليوم", 124 | "1": "غدًا", 125 | "-1": "أمس" 126 | }, 127 | "ar_ma": { 128 | "0": "اليوم", 129 | "1": "غدًا", 130 | "-1": "أمس" 131 | }, 132 | "ar_mr": { 133 | "0": "اليوم", 134 | "1": "غدًا", 135 | "-1": "أمس" 136 | }, 137 | "ar_om": { 138 | "0": "اليوم", 139 | "1": "غدًا", 140 | "-1": "أمس" 141 | }, 142 | "ar_ps": { 143 | "0": "اليوم", 144 | "1": "غدًا", 145 | "-1": "أمس" 146 | }, 147 | "ar_qa": { 148 | "0": "اليوم", 149 | "1": "غدًا", 150 | "-1": "أمس" 151 | }, 152 | "ar_sa": { 153 | "0": "اليوم", 154 | "1": "غدًا", 155 | "-1": "أمس" 156 | }, 157 | "ar_sd": { 158 | "0": "اليوم", 159 | "1": "غدًا", 160 | "-1": "أمس" 161 | }, 162 | "ar_so": { 163 | "0": "اليوم", 164 | "1": "غدًا", 165 | "-1": "أمس" 166 | }, 167 | "ar_ss": { 168 | "0": "اليوم", 169 | "1": "غدًا", 170 | "-1": "أمس" 171 | }, 172 | "ar_sy": { 173 | "0": "اليوم", 174 | "1": "غدًا", 175 | "-1": "أمس" 176 | }, 177 | "ar_td": { 178 | "0": "اليوم", 179 | "1": "غدًا", 180 | "-1": "أمس" 181 | }, 182 | "ar_tn": { 183 | "0": "اليوم", 184 | "1": "غدًا", 185 | "-1": "أمس" 186 | }, 187 | "ar_ye": { 188 | "0": "اليوم", 189 | "1": "غدًا", 190 | "-1": "أمس" 191 | }, 192 | "as": { 193 | "0": "আজি", 194 | "1": "কাইলৈ", 195 | "-1": "কালি" 196 | }, 197 | "as_in": { 198 | "0": "আজি", 199 | "1": "কাইলৈ", 200 | "-1": "কালি" 201 | }, 202 | "asa": { 203 | "0": "Iyoo", 204 | "1": "Yavo", 205 | "-1": "Ighuo" 206 | }, 207 | "asa_tz": { 208 | "0": "Iyoo", 209 | "1": "Yavo", 210 | "-1": "Ighuo" 211 | }, 212 | "ast": { 213 | "0": "güei", 214 | "1": "mañana", 215 | "-1": "ayeri" 216 | }, 217 | "ast_es": { 218 | "0": "güei", 219 | "1": "mañana", 220 | "-1": "ayeri" 221 | }, 222 | "az": { 223 | "0": "bu gün", 224 | "1": "sabah", 225 | "-1": "dünən" 226 | }, 227 | "az_cyrl": { 228 | "0": "today", 229 | "1": "tomorrow", 230 | "-1": "yesterday" 231 | }, 232 | "az_cyrl_az": { 233 | "0": "today", 234 | "1": "tomorrow", 235 | "-1": "yesterday" 236 | }, 237 | "az_latn": { 238 | "0": "bu gün", 239 | "1": "sabah", 240 | "-1": "dünən" 241 | }, 242 | "az_latn_az": { 243 | "0": "bu gün", 244 | "1": "sabah", 245 | "-1": "dünən" 246 | }, 247 | "bas": { 248 | "0": "lɛ̀n", 249 | "1": "yàni", 250 | "-1": "yààni" 251 | }, 252 | "bas_cm": { 253 | "0": "lɛ̀n", 254 | "1": "yàni", 255 | "-1": "yààni" 256 | }, 257 | "be": { 258 | "0": "сёння", 259 | "1": "заўтра", 260 | "-1": "учора" 261 | }, 262 | "be_by": { 263 | "0": "сёння", 264 | "1": "заўтра", 265 | "-1": "учора" 266 | }, 267 | "bem": { 268 | "0": "Lelo", 269 | "1": "tomorrow", 270 | "-1": "yesterday" 271 | }, 272 | "bem_zm": { 273 | "0": "Lelo", 274 | "1": "tomorrow", 275 | "-1": "yesterday" 276 | }, 277 | "bez": { 278 | "0": "Neng’u ni", 279 | "1": "Hilawu", 280 | "-1": "Igolo" 281 | }, 282 | "bez_tz": { 283 | "0": "Neng’u ni", 284 | "1": "Hilawu", 285 | "-1": "Igolo" 286 | }, 287 | "bg": { 288 | "0": "днес", 289 | "1": "утре", 290 | "-1": "вчера" 291 | }, 292 | "bg_bg": { 293 | "0": "днес", 294 | "1": "утре", 295 | "-1": "вчера" 296 | }, 297 | "bm": { 298 | "0": "bi", 299 | "1": "sini", 300 | "-1": "kunu" 301 | }, 302 | "bm_ml": { 303 | "0": "bi", 304 | "1": "sini", 305 | "-1": "kunu" 306 | }, 307 | "bn": { 308 | "0": "আজ", 309 | "1": "আগামীকাল", 310 | "-1": "গতকাল" 311 | }, 312 | "bn_bd": { 313 | "0": "আজ", 314 | "1": "আগামীকাল", 315 | "-1": "গতকাল" 316 | }, 317 | "bn_in": { 318 | "0": "আজ", 319 | "1": "আগামীকাল", 320 | "-1": "গতকাল" 321 | }, 322 | "bo": { 323 | "0": "དེ་རིང་", 324 | "1": "སང་ཉིན་", 325 | "-1": "ཁས་ས་" 326 | }, 327 | "bo_cn": { 328 | "0": "དེ་རིང་", 329 | "1": "སང་ཉིན་", 330 | "-1": "ཁས་ས་" 331 | }, 332 | "bo_in": { 333 | "0": "དེ་རིང་", 334 | "1": "སང་ཉིན་", 335 | "-1": "ཁས་ས་" 336 | }, 337 | "br": { 338 | "0": "hiziv", 339 | "1": "warcʼhoazh", 340 | "-1": "decʼh" 341 | }, 342 | "br_fr": { 343 | "0": "hiziv", 344 | "1": "warcʼhoazh", 345 | "-1": "decʼh" 346 | }, 347 | "brx": { 348 | "0": "दिनै", 349 | "1": "गाबोन", 350 | "-1": "मैया" 351 | }, 352 | "brx_in": { 353 | "0": "दिनै", 354 | "1": "गाबोन", 355 | "-1": "मैया" 356 | }, 357 | "bs": { 358 | "0": "danas", 359 | "1": "sutra", 360 | "-1": "jučer" 361 | }, 362 | "bs_cyrl": { 363 | "0": "данас", 364 | "1": "сутра", 365 | "-1": "јуче" 366 | }, 367 | "bs_cyrl_ba": { 368 | "0": "данас", 369 | "1": "сутра", 370 | "-1": "јуче" 371 | }, 372 | "bs_latn": { 373 | "0": "danas", 374 | "1": "sutra", 375 | "-1": "jučer" 376 | }, 377 | "bs_latn_ba": { 378 | "0": "danas", 379 | "1": "sutra", 380 | "-1": "jučer" 381 | }, 382 | "ca": { 383 | "0": "avui", 384 | "1": "demà", 385 | "-1": "ahir" 386 | }, 387 | "ca_ad": { 388 | "0": "avui", 389 | "1": "demà", 390 | "-1": "ahir" 391 | }, 392 | "ca_es": { 393 | "0": "avui", 394 | "1": "demà", 395 | "-1": "ahir" 396 | }, 397 | "ca_es_valencia": { 398 | "0": "avui", 399 | "1": "demà", 400 | "-1": "ahir" 401 | }, 402 | "ca_fr": { 403 | "0": "avui", 404 | "1": "demà", 405 | "-1": "ahir" 406 | }, 407 | "ca_it": { 408 | "0": "avui", 409 | "1": "demà", 410 | "-1": "ahir" 411 | }, 412 | "ccp": { 413 | "0": "𑄃𑄬𑄌𑄴𑄥𑄳𑄠", 414 | "1": "𑄃𑄬𑄎𑄬𑄖𑄴𑄖𑄳𑄠𑄇𑄬𑄣𑄳𑄠𑄬", 415 | "-1": "𑄉𑄬𑄣𑄴𑄣𑄳𑄠𑄇𑄬𑄣𑄳𑄠𑄬" 416 | }, 417 | "ccp_bd": { 418 | "0": "𑄃𑄬𑄌𑄴𑄥𑄳𑄠", 419 | "1": "𑄃𑄬𑄎𑄬𑄖𑄴𑄖𑄳𑄠𑄇𑄬𑄣𑄳𑄠𑄬", 420 | "-1": "𑄉𑄬𑄣𑄴𑄣𑄳𑄠𑄇𑄬𑄣𑄳𑄠𑄬" 421 | }, 422 | "ccp_in": { 423 | "0": "𑄃𑄬𑄌𑄴𑄥𑄳𑄠", 424 | "1": "𑄃𑄬𑄎𑄬𑄖𑄴𑄖𑄳𑄠𑄇𑄬𑄣𑄳𑄠𑄬", 425 | "-1": "𑄉𑄬𑄣𑄴𑄣𑄳𑄠𑄇𑄬𑄣𑄳𑄠𑄬" 426 | }, 427 | "ce": { 428 | "0": "тахана", 429 | "1": "кхана", 430 | "-1": "селхана" 431 | }, 432 | "ce_ru": { 433 | "0": "тахана", 434 | "1": "кхана", 435 | "-1": "селхана" 436 | }, 437 | "ceb": { 438 | "0": "Karon nga Adlaw", 439 | "1": "Ugma", 440 | "-1": "Kagahapon" 441 | }, 442 | "ceb_ph": { 443 | "0": "Karon nga Adlaw", 444 | "1": "Ugma", 445 | "-1": "Kagahapon" 446 | }, 447 | "cgg": { 448 | "0": "Erizooba", 449 | "1": "Nyenkyakare", 450 | "-1": "Nyomwabazyo" 451 | }, 452 | "cgg_ug": { 453 | "0": "Erizooba", 454 | "1": "Nyenkyakare", 455 | "-1": "Nyomwabazyo" 456 | }, 457 | "chr": { 458 | "0": "ᎪᎯ ᎢᎦ", 459 | "1": "ᏌᎾᎴᎢ", 460 | "-1": "ᏒᎯ" 461 | }, 462 | "chr_us": { 463 | "0": "ᎪᎯ ᎢᎦ", 464 | "1": "ᏌᎾᎴᎢ", 465 | "-1": "ᏒᎯ" 466 | }, 467 | "ckb": { 468 | "0": "ئەمڕۆ", 469 | "1": "سبەی", 470 | "-1": "yesterday" 471 | }, 472 | "ckb_iq": { 473 | "0": "ئەمڕۆ", 474 | "1": "سبەی", 475 | "-1": "yesterday" 476 | }, 477 | "ckb_ir": { 478 | "0": "ئەمڕۆ", 479 | "1": "سبەی", 480 | "-1": "yesterday" 481 | }, 482 | "cs": { 483 | "0": "dnes", 484 | "1": "zítra", 485 | "-1": "včera" 486 | }, 487 | "cs_cz": { 488 | "0": "dnes", 489 | "1": "zítra", 490 | "-1": "včera" 491 | }, 492 | "cu": { 493 | "0": "дне́сь", 494 | "1": "наꙋ́трїе", 495 | "-1": "вчера̀" 496 | }, 497 | "cu_ru": { 498 | "0": "дне́сь", 499 | "1": "наꙋ́трїе", 500 | "-1": "вчера̀" 501 | }, 502 | "cy": { 503 | "0": "heddiw", 504 | "1": "yfory", 505 | "-1": "ddoe" 506 | }, 507 | "cy_gb": { 508 | "0": "heddiw", 509 | "1": "yfory", 510 | "-1": "ddoe" 511 | }, 512 | "da": { 513 | "0": "i dag", 514 | "1": "i morgen", 515 | "-1": "i går" 516 | }, 517 | "da_dk": { 518 | "0": "i dag", 519 | "1": "i morgen", 520 | "-1": "i går" 521 | }, 522 | "da_gl": { 523 | "0": "i dag", 524 | "1": "i morgen", 525 | "-1": "i går" 526 | }, 527 | "dav": { 528 | "0": "Idime", 529 | "1": "Kesho", 530 | "-1": "Iguo" 531 | }, 532 | "dav_ke": { 533 | "0": "Idime", 534 | "1": "Kesho", 535 | "-1": "Iguo" 536 | }, 537 | "de": { 538 | "0": "heute", 539 | "1": "morgen", 540 | "-1": "gestern" 541 | }, 542 | "de_at": { 543 | "0": "heute", 544 | "1": "morgen", 545 | "-1": "gestern" 546 | }, 547 | "de_be": { 548 | "0": "heute", 549 | "1": "morgen", 550 | "-1": "gestern" 551 | }, 552 | "de_ch": { 553 | "0": "heute", 554 | "1": "morgen", 555 | "-1": "gestern" 556 | }, 557 | "de_de": { 558 | "0": "heute", 559 | "1": "morgen", 560 | "-1": "gestern" 561 | }, 562 | "de_it": { 563 | "0": "heute", 564 | "1": "morgen", 565 | "-1": "gestern" 566 | }, 567 | "de_li": { 568 | "0": "heute", 569 | "1": "morgen", 570 | "-1": "gestern" 571 | }, 572 | "de_lu": { 573 | "0": "heute", 574 | "1": "morgen", 575 | "-1": "gestern" 576 | }, 577 | "dje": { 578 | "0": "Hõo", 579 | "1": "Suba", 580 | "-1": "Bi" 581 | }, 582 | "dje_ne": { 583 | "0": "Hõo", 584 | "1": "Suba", 585 | "-1": "Bi" 586 | }, 587 | "dsb": { 588 | "0": "źinsa", 589 | "1": "witśe", 590 | "-1": "cora" 591 | }, 592 | "dsb_de": { 593 | "0": "źinsa", 594 | "1": "witśe", 595 | "-1": "cora" 596 | }, 597 | "dua": { 598 | "0": "wɛ́ŋgɛ̄", 599 | "1": "kíɛlɛ", 600 | "-1": "kíɛlɛ nítómb́í" 601 | }, 602 | "dua_cm": { 603 | "0": "wɛ́ŋgɛ̄", 604 | "1": "kíɛlɛ", 605 | "-1": "kíɛlɛ nítómb́í" 606 | }, 607 | "dyo": { 608 | "0": "Jaat", 609 | "1": "Kajom", 610 | "-1": "Fucen" 611 | }, 612 | "dyo_sn": { 613 | "0": "Jaat", 614 | "1": "Kajom", 615 | "-1": "Fucen" 616 | }, 617 | "dz": { 618 | "0": "ད་རིས་", 619 | "1": "ནངས་པ་", 620 | "-1": "ཁ་ཙ་" 621 | }, 622 | "dz_bt": { 623 | "0": "ད་རིས་", 624 | "1": "ནངས་པ་", 625 | "-1": "ཁ་ཙ་" 626 | }, 627 | "ebu": { 628 | "0": "Ũmũnthĩ", 629 | "1": "Rũciũ", 630 | "-1": "Ĩgoro" 631 | }, 632 | "ebu_ke": { 633 | "0": "Ũmũnthĩ", 634 | "1": "Rũciũ", 635 | "-1": "Ĩgoro" 636 | }, 637 | "ee": { 638 | "0": "egbe", 639 | "1": "etsɔ si gbɔna", 640 | "-1": "etsɔ si va yi" 641 | }, 642 | "ee_gh": { 643 | "0": "egbe", 644 | "1": "etsɔ si gbɔna", 645 | "-1": "etsɔ si va yi" 646 | }, 647 | "ee_tg": { 648 | "0": "egbe", 649 | "1": "etsɔ si gbɔna", 650 | "-1": "etsɔ si va yi" 651 | }, 652 | "el": { 653 | "0": "σήμερα", 654 | "1": "αύριο", 655 | "-1": "χθες" 656 | }, 657 | "el_cy": { 658 | "0": "σήμερα", 659 | "1": "αύριο", 660 | "-1": "χθες" 661 | }, 662 | "el_gr": { 663 | "0": "σήμερα", 664 | "1": "αύριο", 665 | "-1": "χθες" 666 | }, 667 | "en": { 668 | "0": "today", 669 | "1": "tomorrow", 670 | "-1": "yesterday" 671 | }, 672 | "en_001": { 673 | "0": "today", 674 | "1": "tomorrow", 675 | "-1": "yesterday" 676 | }, 677 | "en_150": { 678 | "0": "today", 679 | "1": "tomorrow", 680 | "-1": "yesterday" 681 | }, 682 | "en_ae": { 683 | "0": "today", 684 | "1": "tomorrow", 685 | "-1": "yesterday" 686 | }, 687 | "en_ag": { 688 | "0": "today", 689 | "1": "tomorrow", 690 | "-1": "yesterday" 691 | }, 692 | "en_ai": { 693 | "0": "today", 694 | "1": "tomorrow", 695 | "-1": "yesterday" 696 | }, 697 | "en_as": { 698 | "0": "today", 699 | "1": "tomorrow", 700 | "-1": "yesterday" 701 | }, 702 | "en_at": { 703 | "0": "today", 704 | "1": "tomorrow", 705 | "-1": "yesterday" 706 | }, 707 | "en_au": { 708 | "0": "today", 709 | "1": "tomorrow", 710 | "-1": "yesterday" 711 | }, 712 | "en_bb": { 713 | "0": "today", 714 | "1": "tomorrow", 715 | "-1": "yesterday" 716 | }, 717 | "en_be": { 718 | "0": "today", 719 | "1": "tomorrow", 720 | "-1": "yesterday" 721 | }, 722 | "en_bi": { 723 | "0": "today", 724 | "1": "tomorrow", 725 | "-1": "yesterday" 726 | }, 727 | "en_bm": { 728 | "0": "today", 729 | "1": "tomorrow", 730 | "-1": "yesterday" 731 | }, 732 | "en_bs": { 733 | "0": "today", 734 | "1": "tomorrow", 735 | "-1": "yesterday" 736 | }, 737 | "en_bw": { 738 | "0": "today", 739 | "1": "tomorrow", 740 | "-1": "yesterday" 741 | }, 742 | "en_bz": { 743 | "0": "today", 744 | "1": "tomorrow", 745 | "-1": "yesterday" 746 | }, 747 | "en_ca": { 748 | "0": "today", 749 | "1": "tomorrow", 750 | "-1": "yesterday" 751 | }, 752 | "en_cc": { 753 | "0": "today", 754 | "1": "tomorrow", 755 | "-1": "yesterday" 756 | }, 757 | "en_ch": { 758 | "0": "today", 759 | "1": "tomorrow", 760 | "-1": "yesterday" 761 | }, 762 | "en_ck": { 763 | "0": "today", 764 | "1": "tomorrow", 765 | "-1": "yesterday" 766 | }, 767 | "en_cm": { 768 | "0": "today", 769 | "1": "tomorrow", 770 | "-1": "yesterday" 771 | }, 772 | "en_cx": { 773 | "0": "today", 774 | "1": "tomorrow", 775 | "-1": "yesterday" 776 | }, 777 | "en_cy": { 778 | "0": "today", 779 | "1": "tomorrow", 780 | "-1": "yesterday" 781 | }, 782 | "en_de": { 783 | "0": "today", 784 | "1": "tomorrow", 785 | "-1": "yesterday" 786 | }, 787 | "en_dg": { 788 | "0": "today", 789 | "1": "tomorrow", 790 | "-1": "yesterday" 791 | }, 792 | "en_dk": { 793 | "0": "today", 794 | "1": "tomorrow", 795 | "-1": "yesterday" 796 | }, 797 | "en_dm": { 798 | "0": "today", 799 | "1": "tomorrow", 800 | "-1": "yesterday" 801 | }, 802 | "en_er": { 803 | "0": "today", 804 | "1": "tomorrow", 805 | "-1": "yesterday" 806 | }, 807 | "en_fi": { 808 | "0": "today", 809 | "1": "tomorrow", 810 | "-1": "yesterday" 811 | }, 812 | "en_fj": { 813 | "0": "today", 814 | "1": "tomorrow", 815 | "-1": "yesterday" 816 | }, 817 | "en_fk": { 818 | "0": "today", 819 | "1": "tomorrow", 820 | "-1": "yesterday" 821 | }, 822 | "en_fm": { 823 | "0": "today", 824 | "1": "tomorrow", 825 | "-1": "yesterday" 826 | }, 827 | "en_gb": { 828 | "0": "today", 829 | "1": "tomorrow", 830 | "-1": "yesterday" 831 | }, 832 | "en_gd": { 833 | "0": "today", 834 | "1": "tomorrow", 835 | "-1": "yesterday" 836 | }, 837 | "en_gg": { 838 | "0": "today", 839 | "1": "tomorrow", 840 | "-1": "yesterday" 841 | }, 842 | "en_gh": { 843 | "0": "today", 844 | "1": "tomorrow", 845 | "-1": "yesterday" 846 | }, 847 | "en_gi": { 848 | "0": "today", 849 | "1": "tomorrow", 850 | "-1": "yesterday" 851 | }, 852 | "en_gm": { 853 | "0": "today", 854 | "1": "tomorrow", 855 | "-1": "yesterday" 856 | }, 857 | "en_gu": { 858 | "0": "today", 859 | "1": "tomorrow", 860 | "-1": "yesterday" 861 | }, 862 | "en_gy": { 863 | "0": "today", 864 | "1": "tomorrow", 865 | "-1": "yesterday" 866 | }, 867 | "en_hk": { 868 | "0": "today", 869 | "1": "tomorrow", 870 | "-1": "yesterday" 871 | }, 872 | "en_ie": { 873 | "0": "today", 874 | "1": "tomorrow", 875 | "-1": "yesterday" 876 | }, 877 | "en_il": { 878 | "0": "today", 879 | "1": "tomorrow", 880 | "-1": "yesterday" 881 | }, 882 | "en_im": { 883 | "0": "today", 884 | "1": "tomorrow", 885 | "-1": "yesterday" 886 | }, 887 | "en_in": { 888 | "0": "today", 889 | "1": "tomorrow", 890 | "-1": "yesterday" 891 | }, 892 | "en_io": { 893 | "0": "today", 894 | "1": "tomorrow", 895 | "-1": "yesterday" 896 | }, 897 | "en_je": { 898 | "0": "today", 899 | "1": "tomorrow", 900 | "-1": "yesterday" 901 | }, 902 | "en_jm": { 903 | "0": "today", 904 | "1": "tomorrow", 905 | "-1": "yesterday" 906 | }, 907 | "en_ke": { 908 | "0": "today", 909 | "1": "tomorrow", 910 | "-1": "yesterday" 911 | }, 912 | "en_ki": { 913 | "0": "today", 914 | "1": "tomorrow", 915 | "-1": "yesterday" 916 | }, 917 | "en_kn": { 918 | "0": "today", 919 | "1": "tomorrow", 920 | "-1": "yesterday" 921 | }, 922 | "en_ky": { 923 | "0": "today", 924 | "1": "tomorrow", 925 | "-1": "yesterday" 926 | }, 927 | "en_lc": { 928 | "0": "today", 929 | "1": "tomorrow", 930 | "-1": "yesterday" 931 | }, 932 | "en_lr": { 933 | "0": "today", 934 | "1": "tomorrow", 935 | "-1": "yesterday" 936 | }, 937 | "en_ls": { 938 | "0": "today", 939 | "1": "tomorrow", 940 | "-1": "yesterday" 941 | }, 942 | "en_mg": { 943 | "0": "today", 944 | "1": "tomorrow", 945 | "-1": "yesterday" 946 | }, 947 | "en_mh": { 948 | "0": "today", 949 | "1": "tomorrow", 950 | "-1": "yesterday" 951 | }, 952 | "en_mo": { 953 | "0": "today", 954 | "1": "tomorrow", 955 | "-1": "yesterday" 956 | }, 957 | "en_mp": { 958 | "0": "today", 959 | "1": "tomorrow", 960 | "-1": "yesterday" 961 | }, 962 | "en_ms": { 963 | "0": "today", 964 | "1": "tomorrow", 965 | "-1": "yesterday" 966 | }, 967 | "en_mt": { 968 | "0": "today", 969 | "1": "tomorrow", 970 | "-1": "yesterday" 971 | }, 972 | "en_mu": { 973 | "0": "today", 974 | "1": "tomorrow", 975 | "-1": "yesterday" 976 | }, 977 | "en_mw": { 978 | "0": "today", 979 | "1": "tomorrow", 980 | "-1": "yesterday" 981 | }, 982 | "en_my": { 983 | "0": "today", 984 | "1": "tomorrow", 985 | "-1": "yesterday" 986 | }, 987 | "en_na": { 988 | "0": "today", 989 | "1": "tomorrow", 990 | "-1": "yesterday" 991 | }, 992 | "en_nf": { 993 | "0": "today", 994 | "1": "tomorrow", 995 | "-1": "yesterday" 996 | }, 997 | "en_ng": { 998 | "0": "today", 999 | "1": "tomorrow", 1000 | "-1": "yesterday" 1001 | }, 1002 | "en_nl": { 1003 | "0": "today", 1004 | "1": "tomorrow", 1005 | "-1": "yesterday" 1006 | }, 1007 | "en_nr": { 1008 | "0": "today", 1009 | "1": "tomorrow", 1010 | "-1": "yesterday" 1011 | }, 1012 | "en_nu": { 1013 | "0": "today", 1014 | "1": "tomorrow", 1015 | "-1": "yesterday" 1016 | }, 1017 | "en_nz": { 1018 | "0": "today", 1019 | "1": "tomorrow", 1020 | "-1": "yesterday" 1021 | }, 1022 | "en_pg": { 1023 | "0": "today", 1024 | "1": "tomorrow", 1025 | "-1": "yesterday" 1026 | }, 1027 | "en_ph": { 1028 | "0": "today", 1029 | "1": "tomorrow", 1030 | "-1": "yesterday" 1031 | }, 1032 | "en_pk": { 1033 | "0": "today", 1034 | "1": "tomorrow", 1035 | "-1": "yesterday" 1036 | }, 1037 | "en_pn": { 1038 | "0": "today", 1039 | "1": "tomorrow", 1040 | "-1": "yesterday" 1041 | }, 1042 | "en_pr": { 1043 | "0": "today", 1044 | "1": "tomorrow", 1045 | "-1": "yesterday" 1046 | }, 1047 | "en_pw": { 1048 | "0": "today", 1049 | "1": "tomorrow", 1050 | "-1": "yesterday" 1051 | }, 1052 | "en_rw": { 1053 | "0": "today", 1054 | "1": "tomorrow", 1055 | "-1": "yesterday" 1056 | }, 1057 | "en_sb": { 1058 | "0": "today", 1059 | "1": "tomorrow", 1060 | "-1": "yesterday" 1061 | }, 1062 | "en_sc": { 1063 | "0": "today", 1064 | "1": "tomorrow", 1065 | "-1": "yesterday" 1066 | }, 1067 | "en_sd": { 1068 | "0": "today", 1069 | "1": "tomorrow", 1070 | "-1": "yesterday" 1071 | }, 1072 | "en_se": { 1073 | "0": "today", 1074 | "1": "tomorrow", 1075 | "-1": "yesterday" 1076 | }, 1077 | "en_sg": { 1078 | "0": "today", 1079 | "1": "tomorrow", 1080 | "-1": "yesterday" 1081 | }, 1082 | "en_sh": { 1083 | "0": "today", 1084 | "1": "tomorrow", 1085 | "-1": "yesterday" 1086 | }, 1087 | "en_si": { 1088 | "0": "today", 1089 | "1": "tomorrow", 1090 | "-1": "yesterday" 1091 | }, 1092 | "en_sl": { 1093 | "0": "today", 1094 | "1": "tomorrow", 1095 | "-1": "yesterday" 1096 | }, 1097 | "en_ss": { 1098 | "0": "today", 1099 | "1": "tomorrow", 1100 | "-1": "yesterday" 1101 | }, 1102 | "en_sx": { 1103 | "0": "today", 1104 | "1": "tomorrow", 1105 | "-1": "yesterday" 1106 | }, 1107 | "en_sz": { 1108 | "0": "today", 1109 | "1": "tomorrow", 1110 | "-1": "yesterday" 1111 | }, 1112 | "en_tc": { 1113 | "0": "today", 1114 | "1": "tomorrow", 1115 | "-1": "yesterday" 1116 | }, 1117 | "en_tk": { 1118 | "0": "today", 1119 | "1": "tomorrow", 1120 | "-1": "yesterday" 1121 | }, 1122 | "en_to": { 1123 | "0": "today", 1124 | "1": "tomorrow", 1125 | "-1": "yesterday" 1126 | }, 1127 | "en_tt": { 1128 | "0": "today", 1129 | "1": "tomorrow", 1130 | "-1": "yesterday" 1131 | }, 1132 | "en_tv": { 1133 | "0": "today", 1134 | "1": "tomorrow", 1135 | "-1": "yesterday" 1136 | }, 1137 | "en_tz": { 1138 | "0": "today", 1139 | "1": "tomorrow", 1140 | "-1": "yesterday" 1141 | }, 1142 | "en_ug": { 1143 | "0": "today", 1144 | "1": "tomorrow", 1145 | "-1": "yesterday" 1146 | }, 1147 | "en_um": { 1148 | "0": "today", 1149 | "1": "tomorrow", 1150 | "-1": "yesterday" 1151 | }, 1152 | "en_us": { 1153 | "0": "today", 1154 | "1": "tomorrow", 1155 | "-1": "yesterday" 1156 | }, 1157 | "en_us_posix": { 1158 | "0": "today", 1159 | "1": "tomorrow", 1160 | "-1": "yesterday" 1161 | }, 1162 | "en_vc": { 1163 | "0": "today", 1164 | "1": "tomorrow", 1165 | "-1": "yesterday" 1166 | }, 1167 | "en_vg": { 1168 | "0": "today", 1169 | "1": "tomorrow", 1170 | "-1": "yesterday" 1171 | }, 1172 | "en_vi": { 1173 | "0": "today", 1174 | "1": "tomorrow", 1175 | "-1": "yesterday" 1176 | }, 1177 | "en_vu": { 1178 | "0": "today", 1179 | "1": "tomorrow", 1180 | "-1": "yesterday" 1181 | }, 1182 | "en_ws": { 1183 | "0": "today", 1184 | "1": "tomorrow", 1185 | "-1": "yesterday" 1186 | }, 1187 | "en_za": { 1188 | "0": "today", 1189 | "1": "tomorrow", 1190 | "-1": "yesterday" 1191 | }, 1192 | "en_zm": { 1193 | "0": "today", 1194 | "1": "tomorrow", 1195 | "-1": "yesterday" 1196 | }, 1197 | "en_zw": { 1198 | "0": "today", 1199 | "1": "tomorrow", 1200 | "-1": "yesterday" 1201 | }, 1202 | "eo": { 1203 | "0": "hodiaŭ", 1204 | "1": "morgaŭ", 1205 | "-1": "hieraŭ" 1206 | }, 1207 | "eo_001": { 1208 | "0": "hodiaŭ", 1209 | "1": "morgaŭ", 1210 | "-1": "hieraŭ" 1211 | }, 1212 | "es": { 1213 | "0": "hoy", 1214 | "1": "mañana", 1215 | "-1": "ayer" 1216 | }, 1217 | "es_419": { 1218 | "0": "hoy", 1219 | "1": "mañana", 1220 | "-1": "ayer" 1221 | }, 1222 | "es_ar": { 1223 | "0": "hoy", 1224 | "1": "mañana", 1225 | "-1": "ayer" 1226 | }, 1227 | "es_bo": { 1228 | "0": "hoy", 1229 | "1": "mañana", 1230 | "-1": "ayer" 1231 | }, 1232 | "es_br": { 1233 | "0": "hoy", 1234 | "1": "mañana", 1235 | "-1": "ayer" 1236 | }, 1237 | "es_bz": { 1238 | "0": "hoy", 1239 | "1": "mañana", 1240 | "-1": "ayer" 1241 | }, 1242 | "es_cl": { 1243 | "0": "hoy", 1244 | "1": "mañana", 1245 | "-1": "ayer" 1246 | }, 1247 | "es_co": { 1248 | "0": "hoy", 1249 | "1": "mañana", 1250 | "-1": "ayer" 1251 | }, 1252 | "es_cr": { 1253 | "0": "hoy", 1254 | "1": "mañana", 1255 | "-1": "ayer" 1256 | }, 1257 | "es_cu": { 1258 | "0": "hoy", 1259 | "1": "mañana", 1260 | "-1": "ayer" 1261 | }, 1262 | "es_do": { 1263 | "0": "hoy", 1264 | "1": "mañana", 1265 | "-1": "ayer" 1266 | }, 1267 | "es_ea": { 1268 | "0": "hoy", 1269 | "1": "mañana", 1270 | "-1": "ayer" 1271 | }, 1272 | "es_ec": { 1273 | "0": "hoy", 1274 | "1": "mañana", 1275 | "-1": "ayer" 1276 | }, 1277 | "es_es": { 1278 | "0": "hoy", 1279 | "1": "mañana", 1280 | "-1": "ayer" 1281 | }, 1282 | "es_gq": { 1283 | "0": "hoy", 1284 | "1": "mañana", 1285 | "-1": "ayer" 1286 | }, 1287 | "es_gt": { 1288 | "0": "hoy", 1289 | "1": "mañana", 1290 | "-1": "ayer" 1291 | }, 1292 | "es_hn": { 1293 | "0": "hoy", 1294 | "1": "mañana", 1295 | "-1": "ayer" 1296 | }, 1297 | "es_ic": { 1298 | "0": "hoy", 1299 | "1": "mañana", 1300 | "-1": "ayer" 1301 | }, 1302 | "es_mx": { 1303 | "0": "hoy", 1304 | "1": "mañana", 1305 | "-1": "ayer" 1306 | }, 1307 | "es_ni": { 1308 | "0": "hoy", 1309 | "1": "mañana", 1310 | "-1": "ayer" 1311 | }, 1312 | "es_pa": { 1313 | "0": "hoy", 1314 | "1": "mañana", 1315 | "-1": "ayer" 1316 | }, 1317 | "es_pe": { 1318 | "0": "hoy", 1319 | "1": "mañana", 1320 | "-1": "ayer" 1321 | }, 1322 | "es_ph": { 1323 | "0": "hoy", 1324 | "1": "mañana", 1325 | "-1": "ayer" 1326 | }, 1327 | "es_pr": { 1328 | "0": "hoy", 1329 | "1": "mañana", 1330 | "-1": "ayer" 1331 | }, 1332 | "es_py": { 1333 | "0": "hoy", 1334 | "1": "mañana", 1335 | "-1": "ayer" 1336 | }, 1337 | "es_sv": { 1338 | "0": "hoy", 1339 | "1": "mañana", 1340 | "-1": "ayer" 1341 | }, 1342 | "es_us": { 1343 | "0": "hoy", 1344 | "1": "mañana", 1345 | "-1": "ayer" 1346 | }, 1347 | "es_uy": { 1348 | "0": "hoy", 1349 | "1": "mañana", 1350 | "-1": "ayer" 1351 | }, 1352 | "es_ve": { 1353 | "0": "hoy", 1354 | "1": "mañana", 1355 | "-1": "ayer" 1356 | }, 1357 | "et": { 1358 | "0": "täna", 1359 | "1": "homme", 1360 | "-1": "eile" 1361 | }, 1362 | "et_ee": { 1363 | "0": "täna", 1364 | "1": "homme", 1365 | "-1": "eile" 1366 | }, 1367 | "eu": { 1368 | "0": "gaur", 1369 | "1": "bihar", 1370 | "-1": "atzo" 1371 | }, 1372 | "eu_es": { 1373 | "0": "gaur", 1374 | "1": "bihar", 1375 | "-1": "atzo" 1376 | }, 1377 | "ewo": { 1378 | "0": "Aná", 1379 | "1": "Okírí", 1380 | "-1": "Angogé" 1381 | }, 1382 | "ewo_cm": { 1383 | "0": "Aná", 1384 | "1": "Okírí", 1385 | "-1": "Angogé" 1386 | }, 1387 | "fa": { 1388 | "0": "امروز", 1389 | "1": "فردا", 1390 | "-1": "دیروز" 1391 | }, 1392 | "fa_af": { 1393 | "0": "امروز", 1394 | "1": "فردا", 1395 | "-1": "دیروز" 1396 | }, 1397 | "fa_ir": { 1398 | "0": "امروز", 1399 | "1": "فردا", 1400 | "-1": "دیروز" 1401 | }, 1402 | "ff": { 1403 | "0": "Hannde", 1404 | "1": "Jaŋngo", 1405 | "-1": "Haŋki" 1406 | }, 1407 | "ff_latn": { 1408 | "0": "Hannde", 1409 | "1": "Jaŋngo", 1410 | "-1": "Haŋki" 1411 | }, 1412 | "ff_latn_bf": { 1413 | "0": "Hannde", 1414 | "1": "Jaŋngo", 1415 | "-1": "Haŋki" 1416 | }, 1417 | "ff_latn_cm": { 1418 | "0": "Hannde", 1419 | "1": "Jaŋngo", 1420 | "-1": "Haŋki" 1421 | }, 1422 | "ff_latn_gh": { 1423 | "0": "Hannde", 1424 | "1": "Jaŋngo", 1425 | "-1": "Haŋki" 1426 | }, 1427 | "ff_latn_gm": { 1428 | "0": "Hannde", 1429 | "1": "Jaŋngo", 1430 | "-1": "Haŋki" 1431 | }, 1432 | "ff_latn_gn": { 1433 | "0": "Hannde", 1434 | "1": "Jaŋngo", 1435 | "-1": "Haŋki" 1436 | }, 1437 | "ff_latn_gw": { 1438 | "0": "Hannde", 1439 | "1": "Jaŋngo", 1440 | "-1": "Haŋki" 1441 | }, 1442 | "ff_latn_lr": { 1443 | "0": "Hannde", 1444 | "1": "Jaŋngo", 1445 | "-1": "Haŋki" 1446 | }, 1447 | "ff_latn_mr": { 1448 | "0": "Hannde", 1449 | "1": "Jaŋngo", 1450 | "-1": "Haŋki" 1451 | }, 1452 | "ff_latn_ne": { 1453 | "0": "Hannde", 1454 | "1": "Jaŋngo", 1455 | "-1": "Haŋki" 1456 | }, 1457 | "ff_latn_ng": { 1458 | "0": "Hannde", 1459 | "1": "Jaŋngo", 1460 | "-1": "Haŋki" 1461 | }, 1462 | "ff_latn_sl": { 1463 | "0": "Hannde", 1464 | "1": "Jaŋngo", 1465 | "-1": "Haŋki" 1466 | }, 1467 | "ff_latn_sn": { 1468 | "0": "Hannde", 1469 | "1": "Jaŋngo", 1470 | "-1": "Haŋki" 1471 | }, 1472 | "fi": { 1473 | "0": "tänään", 1474 | "1": "huomenna", 1475 | "-1": "eilen" 1476 | }, 1477 | "fi_fi": { 1478 | "0": "tänään", 1479 | "1": "huomenna", 1480 | "-1": "eilen" 1481 | }, 1482 | "fil": { 1483 | "0": "ngayong araw", 1484 | "1": "bukas", 1485 | "-1": "kahapon" 1486 | }, 1487 | "fil_ph": { 1488 | "0": "ngayong araw", 1489 | "1": "bukas", 1490 | "-1": "kahapon" 1491 | }, 1492 | "fo": { 1493 | "0": "í dag", 1494 | "1": "í morgin", 1495 | "-1": "í gjár" 1496 | }, 1497 | "fo_dk": { 1498 | "0": "í dag", 1499 | "1": "í morgin", 1500 | "-1": "í gjár" 1501 | }, 1502 | "fo_fo": { 1503 | "0": "í dag", 1504 | "1": "í morgin", 1505 | "-1": "í gjár" 1506 | }, 1507 | "fr": { 1508 | "0": "aujourd’hui", 1509 | "1": "demain", 1510 | "-1": "hier" 1511 | }, 1512 | "fr_be": { 1513 | "0": "aujourd’hui", 1514 | "1": "demain", 1515 | "-1": "hier" 1516 | }, 1517 | "fr_bf": { 1518 | "0": "aujourd’hui", 1519 | "1": "demain", 1520 | "-1": "hier" 1521 | }, 1522 | "fr_bi": { 1523 | "0": "aujourd’hui", 1524 | "1": "demain", 1525 | "-1": "hier" 1526 | }, 1527 | "fr_bj": { 1528 | "0": "aujourd’hui", 1529 | "1": "demain", 1530 | "-1": "hier" 1531 | }, 1532 | "fr_bl": { 1533 | "0": "aujourd’hui", 1534 | "1": "demain", 1535 | "-1": "hier" 1536 | }, 1537 | "fr_ca": { 1538 | "0": "aujourd’hui", 1539 | "1": "demain", 1540 | "-1": "hier" 1541 | }, 1542 | "fr_cd": { 1543 | "0": "aujourd’hui", 1544 | "1": "demain", 1545 | "-1": "hier" 1546 | }, 1547 | "fr_cf": { 1548 | "0": "aujourd’hui", 1549 | "1": "demain", 1550 | "-1": "hier" 1551 | }, 1552 | "fr_cg": { 1553 | "0": "aujourd’hui", 1554 | "1": "demain", 1555 | "-1": "hier" 1556 | }, 1557 | "fr_ch": { 1558 | "0": "aujourd’hui", 1559 | "1": "demain", 1560 | "-1": "hier" 1561 | }, 1562 | "fr_ci": { 1563 | "0": "aujourd’hui", 1564 | "1": "demain", 1565 | "-1": "hier" 1566 | }, 1567 | "fr_cm": { 1568 | "0": "aujourd’hui", 1569 | "1": "demain", 1570 | "-1": "hier" 1571 | }, 1572 | "fr_dj": { 1573 | "0": "aujourd’hui", 1574 | "1": "demain", 1575 | "-1": "hier" 1576 | }, 1577 | "fr_dz": { 1578 | "0": "aujourd’hui", 1579 | "1": "demain", 1580 | "-1": "hier" 1581 | }, 1582 | "fr_fr": { 1583 | "0": "aujourd’hui", 1584 | "1": "demain", 1585 | "-1": "hier" 1586 | }, 1587 | "fr_ga": { 1588 | "0": "aujourd’hui", 1589 | "1": "demain", 1590 | "-1": "hier" 1591 | }, 1592 | "fr_gf": { 1593 | "0": "aujourd’hui", 1594 | "1": "demain", 1595 | "-1": "hier" 1596 | }, 1597 | "fr_gn": { 1598 | "0": "aujourd’hui", 1599 | "1": "demain", 1600 | "-1": "hier" 1601 | }, 1602 | "fr_gp": { 1603 | "0": "aujourd’hui", 1604 | "1": "demain", 1605 | "-1": "hier" 1606 | }, 1607 | "fr_gq": { 1608 | "0": "aujourd’hui", 1609 | "1": "demain", 1610 | "-1": "hier" 1611 | }, 1612 | "fr_ht": { 1613 | "0": "aujourd’hui", 1614 | "1": "demain", 1615 | "-1": "hier" 1616 | }, 1617 | "fr_km": { 1618 | "0": "aujourd’hui", 1619 | "1": "demain", 1620 | "-1": "hier" 1621 | }, 1622 | "fr_lu": { 1623 | "0": "aujourd’hui", 1624 | "1": "demain", 1625 | "-1": "hier" 1626 | }, 1627 | "fr_ma": { 1628 | "0": "aujourd’hui", 1629 | "1": "demain", 1630 | "-1": "hier" 1631 | }, 1632 | "fr_mc": { 1633 | "0": "aujourd’hui", 1634 | "1": "demain", 1635 | "-1": "hier" 1636 | }, 1637 | "fr_mf": { 1638 | "0": "aujourd’hui", 1639 | "1": "demain", 1640 | "-1": "hier" 1641 | }, 1642 | "fr_mg": { 1643 | "0": "aujourd’hui", 1644 | "1": "demain", 1645 | "-1": "hier" 1646 | }, 1647 | "fr_ml": { 1648 | "0": "aujourd’hui", 1649 | "1": "demain", 1650 | "-1": "hier" 1651 | }, 1652 | "fr_mq": { 1653 | "0": "aujourd’hui", 1654 | "1": "demain", 1655 | "-1": "hier" 1656 | }, 1657 | "fr_mr": { 1658 | "0": "aujourd’hui", 1659 | "1": "demain", 1660 | "-1": "hier" 1661 | }, 1662 | "fr_mu": { 1663 | "0": "aujourd’hui", 1664 | "1": "demain", 1665 | "-1": "hier" 1666 | }, 1667 | "fr_nc": { 1668 | "0": "aujourd’hui", 1669 | "1": "demain", 1670 | "-1": "hier" 1671 | }, 1672 | "fr_ne": { 1673 | "0": "aujourd’hui", 1674 | "1": "demain", 1675 | "-1": "hier" 1676 | }, 1677 | "fr_pf": { 1678 | "0": "aujourd’hui", 1679 | "1": "demain", 1680 | "-1": "hier" 1681 | }, 1682 | "fr_pm": { 1683 | "0": "aujourd’hui", 1684 | "1": "demain", 1685 | "-1": "hier" 1686 | }, 1687 | "fr_re": { 1688 | "0": "aujourd’hui", 1689 | "1": "demain", 1690 | "-1": "hier" 1691 | }, 1692 | "fr_rw": { 1693 | "0": "aujourd’hui", 1694 | "1": "demain", 1695 | "-1": "hier" 1696 | }, 1697 | "fr_sc": { 1698 | "0": "aujourd’hui", 1699 | "1": "demain", 1700 | "-1": "hier" 1701 | }, 1702 | "fr_sn": { 1703 | "0": "aujourd’hui", 1704 | "1": "demain", 1705 | "-1": "hier" 1706 | }, 1707 | "fr_sy": { 1708 | "0": "aujourd’hui", 1709 | "1": "demain", 1710 | "-1": "hier" 1711 | }, 1712 | "fr_td": { 1713 | "0": "aujourd’hui", 1714 | "1": "demain", 1715 | "-1": "hier" 1716 | }, 1717 | "fr_tg": { 1718 | "0": "aujourd’hui", 1719 | "1": "demain", 1720 | "-1": "hier" 1721 | }, 1722 | "fr_tn": { 1723 | "0": "aujourd’hui", 1724 | "1": "demain", 1725 | "-1": "hier" 1726 | }, 1727 | "fr_vu": { 1728 | "0": "aujourd’hui", 1729 | "1": "demain", 1730 | "-1": "hier" 1731 | }, 1732 | "fr_wf": { 1733 | "0": "aujourd’hui", 1734 | "1": "demain", 1735 | "-1": "hier" 1736 | }, 1737 | "fr_yt": { 1738 | "0": "aujourd’hui", 1739 | "1": "demain", 1740 | "-1": "hier" 1741 | }, 1742 | "fur": { 1743 | "0": "vuê", 1744 | "1": "doman", 1745 | "-1": "îr" 1746 | }, 1747 | "fur_it": { 1748 | "0": "vuê", 1749 | "1": "doman", 1750 | "-1": "îr" 1751 | }, 1752 | "fy": { 1753 | "0": "vandaag", 1754 | "1": "morgen", 1755 | "-1": "gisteren" 1756 | }, 1757 | "fy_nl": { 1758 | "0": "vandaag", 1759 | "1": "morgen", 1760 | "-1": "gisteren" 1761 | }, 1762 | "ga": { 1763 | "0": "inniu", 1764 | "1": "amárach", 1765 | "-1": "inné" 1766 | }, 1767 | "ga_ie": { 1768 | "0": "inniu", 1769 | "1": "amárach", 1770 | "-1": "inné" 1771 | }, 1772 | "gd": { 1773 | "0": "an-diugh", 1774 | "1": "a-màireach", 1775 | "-1": "an-dè" 1776 | }, 1777 | "gd_gb": { 1778 | "0": "an-diugh", 1779 | "1": "a-màireach", 1780 | "-1": "an-dè" 1781 | }, 1782 | "gl": { 1783 | "0": "hoxe", 1784 | "1": "mañá", 1785 | "-1": "onte" 1786 | }, 1787 | "gl_es": { 1788 | "0": "hoxe", 1789 | "1": "mañá", 1790 | "-1": "onte" 1791 | }, 1792 | "gsw": { 1793 | "0": "hüt", 1794 | "1": "moorn", 1795 | "-1": "geschter" 1796 | }, 1797 | "gsw_ch": { 1798 | "0": "hüt", 1799 | "1": "moorn", 1800 | "-1": "geschter" 1801 | }, 1802 | "gsw_fr": { 1803 | "0": "hüt", 1804 | "1": "moorn", 1805 | "-1": "geschter" 1806 | }, 1807 | "gsw_li": { 1808 | "0": "hüt", 1809 | "1": "moorn", 1810 | "-1": "geschter" 1811 | }, 1812 | "gu": { 1813 | "0": "આજે", 1814 | "1": "આવતીકાલે", 1815 | "-1": "ગઈકાલે" 1816 | }, 1817 | "gu_in": { 1818 | "0": "આજે", 1819 | "1": "આવતીકાલે", 1820 | "-1": "ગઈકાલે" 1821 | }, 1822 | "guz": { 1823 | "0": "Rero", 1824 | "1": "Mambia", 1825 | "-1": "Igoro" 1826 | }, 1827 | "guz_ke": { 1828 | "0": "Rero", 1829 | "1": "Mambia", 1830 | "-1": "Igoro" 1831 | }, 1832 | "gv": { 1833 | "0": "today", 1834 | "1": "tomorrow", 1835 | "-1": "yesterday" 1836 | }, 1837 | "gv_im": { 1838 | "0": "today", 1839 | "1": "tomorrow", 1840 | "-1": "yesterday" 1841 | }, 1842 | "ha": { 1843 | "0": "Yau", 1844 | "1": "Gobe", 1845 | "-1": "Jiya" 1846 | }, 1847 | "ha_gh": { 1848 | "0": "Yau", 1849 | "1": "Gobe", 1850 | "-1": "Jiya" 1851 | }, 1852 | "ha_ne": { 1853 | "0": "Yau", 1854 | "1": "Gobe", 1855 | "-1": "Jiya" 1856 | }, 1857 | "ha_ng": { 1858 | "0": "Yau", 1859 | "1": "Gobe", 1860 | "-1": "Jiya" 1861 | }, 1862 | "haw": { 1863 | "0": "today", 1864 | "1": "tomorrow", 1865 | "-1": "yesterday" 1866 | }, 1867 | "haw_us": { 1868 | "0": "today", 1869 | "1": "tomorrow", 1870 | "-1": "yesterday" 1871 | }, 1872 | "he": { 1873 | "0": "היום", 1874 | "1": "מחר", 1875 | "-1": "אתמול" 1876 | }, 1877 | "he_il": { 1878 | "0": "היום", 1879 | "1": "מחר", 1880 | "-1": "אתמול" 1881 | }, 1882 | "hi": { 1883 | "0": "आज", 1884 | "1": "कल", 1885 | "-1": "कल" 1886 | }, 1887 | "hi_in": { 1888 | "0": "आज", 1889 | "1": "कल", 1890 | "-1": "कल" 1891 | }, 1892 | "hr": { 1893 | "0": "danas", 1894 | "1": "sutra", 1895 | "-1": "jučer" 1896 | }, 1897 | "hr_ba": { 1898 | "0": "danas", 1899 | "1": "sutra", 1900 | "-1": "jučer" 1901 | }, 1902 | "hr_hr": { 1903 | "0": "danas", 1904 | "1": "sutra", 1905 | "-1": "jučer" 1906 | }, 1907 | "hsb": { 1908 | "0": "dźensa", 1909 | "1": "jutře", 1910 | "-1": "wčera" 1911 | }, 1912 | "hsb_de": { 1913 | "0": "dźensa", 1914 | "1": "jutře", 1915 | "-1": "wčera" 1916 | }, 1917 | "hu": { 1918 | "0": "ma", 1919 | "1": "holnap", 1920 | "-1": "tegnap" 1921 | }, 1922 | "hu_hu": { 1923 | "0": "ma", 1924 | "1": "holnap", 1925 | "-1": "tegnap" 1926 | }, 1927 | "hy": { 1928 | "0": "այսօր", 1929 | "1": "վաղը", 1930 | "-1": "երեկ" 1931 | }, 1932 | "hy_am": { 1933 | "0": "այսօր", 1934 | "1": "վաղը", 1935 | "-1": "երեկ" 1936 | }, 1937 | "ia": { 1938 | "0": "hodie", 1939 | "1": "deman", 1940 | "-1": "heri" 1941 | }, 1942 | "ia_001": { 1943 | "0": "hodie", 1944 | "1": "deman", 1945 | "-1": "heri" 1946 | }, 1947 | "id": { 1948 | "0": "hari ini", 1949 | "1": "besok", 1950 | "-1": "kemarin" 1951 | }, 1952 | "id_id": { 1953 | "0": "hari ini", 1954 | "1": "besok", 1955 | "-1": "kemarin" 1956 | }, 1957 | "ig": { 1958 | "0": "Taa", 1959 | "1": "Echi", 1960 | "-1": "Ụnyaahụ" 1961 | }, 1962 | "ig_ng": { 1963 | "0": "Taa", 1964 | "1": "Echi", 1965 | "-1": "Ụnyaahụ" 1966 | }, 1967 | "ii": { 1968 | "0": "ꀃꑍ", 1969 | "1": "ꃆꏂꑍ", 1970 | "-1": "ꀋꅔꉈ" 1971 | }, 1972 | "ii_cn": { 1973 | "0": "ꀃꑍ", 1974 | "1": "ꃆꏂꑍ", 1975 | "-1": "ꀋꅔꉈ" 1976 | }, 1977 | "is": { 1978 | "0": "í dag", 1979 | "1": "á morgun", 1980 | "-1": "í gær" 1981 | }, 1982 | "is_is": { 1983 | "0": "í dag", 1984 | "1": "á morgun", 1985 | "-1": "í gær" 1986 | }, 1987 | "it": { 1988 | "0": "oggi", 1989 | "1": "domani", 1990 | "-1": "ieri" 1991 | }, 1992 | "it_ch": { 1993 | "0": "oggi", 1994 | "1": "domani", 1995 | "-1": "ieri" 1996 | }, 1997 | "it_it": { 1998 | "0": "oggi", 1999 | "1": "domani", 2000 | "-1": "ieri" 2001 | }, 2002 | "it_sm": { 2003 | "0": "oggi", 2004 | "1": "domani", 2005 | "-1": "ieri" 2006 | }, 2007 | "it_va": { 2008 | "0": "oggi", 2009 | "1": "domani", 2010 | "-1": "ieri" 2011 | }, 2012 | "ja": { 2013 | "0": "今日", 2014 | "1": "明日", 2015 | "-1": "昨日" 2016 | }, 2017 | "ja_jp": { 2018 | "0": "今日", 2019 | "1": "明日", 2020 | "-1": "昨日" 2021 | }, 2022 | "jgo": { 2023 | "0": "lɔꞋɔ", 2024 | "1": "tomorrow", 2025 | "-1": "yesterday" 2026 | }, 2027 | "jgo_cm": { 2028 | "0": "lɔꞋɔ", 2029 | "1": "tomorrow", 2030 | "-1": "yesterday" 2031 | }, 2032 | "jmc": { 2033 | "0": "Inu", 2034 | "1": "Ngama", 2035 | "-1": "Ukou" 2036 | }, 2037 | "jmc_tz": { 2038 | "0": "Inu", 2039 | "1": "Ngama", 2040 | "-1": "Ukou" 2041 | }, 2042 | "jv": { 2043 | "0": "saiki", 2044 | "1": "sesuk", 2045 | "-1": "wingi" 2046 | }, 2047 | "jv_id": { 2048 | "0": "saiki", 2049 | "1": "sesuk", 2050 | "-1": "wingi" 2051 | }, 2052 | "ka": { 2053 | "0": "დღეს", 2054 | "1": "ხვალ", 2055 | "-1": "გუშინ" 2056 | }, 2057 | "ka_ge": { 2058 | "0": "დღეს", 2059 | "1": "ხვალ", 2060 | "-1": "გუშინ" 2061 | }, 2062 | "kab": { 2063 | "0": "Ass-a", 2064 | "1": "Azekka", 2065 | "-1": "Iḍelli" 2066 | }, 2067 | "kab_dz": { 2068 | "0": "Ass-a", 2069 | "1": "Azekka", 2070 | "-1": "Iḍelli" 2071 | }, 2072 | "kam": { 2073 | "0": "Ũmũnthĩ", 2074 | "1": "Ũnĩ", 2075 | "-1": "Ĩyoo" 2076 | }, 2077 | "kam_ke": { 2078 | "0": "Ũmũnthĩ", 2079 | "1": "Ũnĩ", 2080 | "-1": "Ĩyoo" 2081 | }, 2082 | "kde": { 2083 | "0": "Nelo", 2084 | "1": "Nundu", 2085 | "-1": "Lido" 2086 | }, 2087 | "kde_tz": { 2088 | "0": "Nelo", 2089 | "1": "Nundu", 2090 | "-1": "Lido" 2091 | }, 2092 | "kea": { 2093 | "0": "oji", 2094 | "1": "manha", 2095 | "-1": "onti" 2096 | }, 2097 | "kea_cv": { 2098 | "0": "oji", 2099 | "1": "manha", 2100 | "-1": "onti" 2101 | }, 2102 | "khq": { 2103 | "0": "Hõo", 2104 | "1": "Suba", 2105 | "-1": "Bi" 2106 | }, 2107 | "khq_ml": { 2108 | "0": "Hõo", 2109 | "1": "Suba", 2110 | "-1": "Bi" 2111 | }, 2112 | "ki": { 2113 | "0": "Ũmũthĩ", 2114 | "1": "Rũciũ", 2115 | "-1": "Ira" 2116 | }, 2117 | "ki_ke": { 2118 | "0": "Ũmũthĩ", 2119 | "1": "Rũciũ", 2120 | "-1": "Ira" 2121 | }, 2122 | "kk": { 2123 | "0": "бүгін", 2124 | "1": "ертең", 2125 | "-1": "кеше" 2126 | }, 2127 | "kk_kz": { 2128 | "0": "бүгін", 2129 | "1": "ертең", 2130 | "-1": "кеше" 2131 | }, 2132 | "kkj": { 2133 | "0": "muka", 2134 | "1": "nɛmɛnɔ", 2135 | "-1": "kwey" 2136 | }, 2137 | "kkj_cm": { 2138 | "0": "muka", 2139 | "1": "nɛmɛnɔ", 2140 | "-1": "kwey" 2141 | }, 2142 | "kl": { 2143 | "0": "ullumi", 2144 | "1": "aqagu", 2145 | "-1": "ippassaq" 2146 | }, 2147 | "kl_gl": { 2148 | "0": "ullumi", 2149 | "1": "aqagu", 2150 | "-1": "ippassaq" 2151 | }, 2152 | "kln": { 2153 | "0": "Raini", 2154 | "1": "Mutai", 2155 | "-1": "Amut" 2156 | }, 2157 | "kln_ke": { 2158 | "0": "Raini", 2159 | "1": "Mutai", 2160 | "-1": "Amut" 2161 | }, 2162 | "km": { 2163 | "0": "ថ្ងៃ​នេះ", 2164 | "1": "ថ្ងៃ​ស្អែក", 2165 | "-1": "ម្សិលមិញ" 2166 | }, 2167 | "km_kh": { 2168 | "0": "ថ្ងៃ​នេះ", 2169 | "1": "ថ្ងៃ​ស្អែក", 2170 | "-1": "ម្សិលមិញ" 2171 | }, 2172 | "kn": { 2173 | "0": "ಇಂದು", 2174 | "1": "ನಾಳೆ", 2175 | "-1": "ನಿನ್ನೆ" 2176 | }, 2177 | "kn_in": { 2178 | "0": "ಇಂದು", 2179 | "1": "ನಾಳೆ", 2180 | "-1": "ನಿನ್ನೆ" 2181 | }, 2182 | "ko": { 2183 | "0": "오늘", 2184 | "1": "내일", 2185 | "-1": "어제" 2186 | }, 2187 | "ko_kp": { 2188 | "0": "오늘", 2189 | "1": "내일", 2190 | "-1": "어제" 2191 | }, 2192 | "ko_kr": { 2193 | "0": "오늘", 2194 | "1": "내일", 2195 | "-1": "어제" 2196 | }, 2197 | "kok": { 2198 | "0": "आयज", 2199 | "1": "फाल्यां", 2200 | "-1": "काल" 2201 | }, 2202 | "kok_in": { 2203 | "0": "आयज", 2204 | "1": "फाल्यां", 2205 | "-1": "काल" 2206 | }, 2207 | "ks": { 2208 | "0": "اَز", 2209 | "1": "پگاہ", 2210 | "-1": "راتھ" 2211 | }, 2212 | "ks_in": { 2213 | "0": "اَز", 2214 | "1": "پگاہ", 2215 | "-1": "راتھ" 2216 | }, 2217 | "ksb": { 2218 | "0": "Evi eo", 2219 | "1": "Keloi", 2220 | "-1": "Ghuo" 2221 | }, 2222 | "ksb_tz": { 2223 | "0": "Evi eo", 2224 | "1": "Keloi", 2225 | "-1": "Ghuo" 2226 | }, 2227 | "ksf": { 2228 | "0": "Gɛ́ɛnǝ", 2229 | "1": "Ridúrǝ́", 2230 | "-1": "Rinkɔɔ́" 2231 | }, 2232 | "ksf_cm": { 2233 | "0": "Gɛ́ɛnǝ", 2234 | "1": "Ridúrǝ́", 2235 | "-1": "Rinkɔɔ́" 2236 | }, 2237 | "ksh": { 2238 | "0": "hück", 2239 | "1": "morje", 2240 | "-1": "jestere" 2241 | }, 2242 | "ksh_de": { 2243 | "0": "hück", 2244 | "1": "morje", 2245 | "-1": "jestere" 2246 | }, 2247 | "ku": { 2248 | "0": "îro", 2249 | "1": "sibe", 2250 | "-1": "duh" 2251 | }, 2252 | "ku_tr": { 2253 | "0": "îro", 2254 | "1": "sibe", 2255 | "-1": "duh" 2256 | }, 2257 | "kw": { 2258 | "0": "today", 2259 | "1": "tomorrow", 2260 | "-1": "yesterday" 2261 | }, 2262 | "kw_gb": { 2263 | "0": "today", 2264 | "1": "tomorrow", 2265 | "-1": "yesterday" 2266 | }, 2267 | "ky": { 2268 | "0": "бүгүн", 2269 | "1": "эртең", 2270 | "-1": "кечээ" 2271 | }, 2272 | "ky_kg": { 2273 | "0": "бүгүн", 2274 | "1": "эртең", 2275 | "-1": "кечээ" 2276 | }, 2277 | "lag": { 2278 | "0": "Isikʉ", 2279 | "1": "Lamʉtoondo", 2280 | "-1": "Niijo" 2281 | }, 2282 | "lag_tz": { 2283 | "0": "Isikʉ", 2284 | "1": "Lamʉtoondo", 2285 | "-1": "Niijo" 2286 | }, 2287 | "lb": { 2288 | "0": "haut", 2289 | "1": "muer", 2290 | "-1": "gëschter" 2291 | }, 2292 | "lb_lu": { 2293 | "0": "haut", 2294 | "1": "muer", 2295 | "-1": "gëschter" 2296 | }, 2297 | "lg": { 2298 | "0": "Lwaleero", 2299 | "1": "Nkya", 2300 | "-1": "Ggulo" 2301 | }, 2302 | "lg_ug": { 2303 | "0": "Lwaleero", 2304 | "1": "Nkya", 2305 | "-1": "Ggulo" 2306 | }, 2307 | "lkt": { 2308 | "0": "Lé aŋpétu kiŋ", 2309 | "1": "Híŋhaŋni kiŋháŋ", 2310 | "-1": "Ȟtálehaŋ" 2311 | }, 2312 | "lkt_us": { 2313 | "0": "Lé aŋpétu kiŋ", 2314 | "1": "Híŋhaŋni kiŋháŋ", 2315 | "-1": "Ȟtálehaŋ" 2316 | }, 2317 | "ln": { 2318 | "0": "Lɛlɔ́", 2319 | "1": "Lóbi ekoyâ", 2320 | "-1": "Lóbi elékí" 2321 | }, 2322 | "ln_ao": { 2323 | "0": "Lɛlɔ́", 2324 | "1": "Lóbi ekoyâ", 2325 | "-1": "Lóbi elékí" 2326 | }, 2327 | "ln_cd": { 2328 | "0": "Lɛlɔ́", 2329 | "1": "Lóbi ekoyâ", 2330 | "-1": "Lóbi elékí" 2331 | }, 2332 | "ln_cf": { 2333 | "0": "Lɛlɔ́", 2334 | "1": "Lóbi ekoyâ", 2335 | "-1": "Lóbi elékí" 2336 | }, 2337 | "ln_cg": { 2338 | "0": "Lɛlɔ́", 2339 | "1": "Lóbi ekoyâ", 2340 | "-1": "Lóbi elékí" 2341 | }, 2342 | "lo": { 2343 | "0": "ມື້ນີ້", 2344 | "1": "ມື້ອື່ນ", 2345 | "-1": "ມື້ວານ" 2346 | }, 2347 | "lo_la": { 2348 | "0": "ມື້ນີ້", 2349 | "1": "ມື້ອື່ນ", 2350 | "-1": "ມື້ວານ" 2351 | }, 2352 | "lrc": { 2353 | "0": "أمروٙ", 2354 | "1": "شوٙصوٙ", 2355 | "-1": "دیروٙز" 2356 | }, 2357 | "lrc_iq": { 2358 | "0": "أمروٙ", 2359 | "1": "شوٙصوٙ", 2360 | "-1": "دیروٙز" 2361 | }, 2362 | "lrc_ir": { 2363 | "0": "أمروٙ", 2364 | "1": "شوٙصوٙ", 2365 | "-1": "دیروٙز" 2366 | }, 2367 | "lt": { 2368 | "0": "šiandien", 2369 | "1": "rytoj", 2370 | "-1": "vakar" 2371 | }, 2372 | "lt_lt": { 2373 | "0": "šiandien", 2374 | "1": "rytoj", 2375 | "-1": "vakar" 2376 | }, 2377 | "lu": { 2378 | "0": "Lelu", 2379 | "1": "Malaba", 2380 | "-1": "Makelela" 2381 | }, 2382 | "lu_cd": { 2383 | "0": "Lelu", 2384 | "1": "Malaba", 2385 | "-1": "Makelela" 2386 | }, 2387 | "luo": { 2388 | "0": "kawuono", 2389 | "1": "kiny", 2390 | "-1": "nyoro" 2391 | }, 2392 | "luo_ke": { 2393 | "0": "kawuono", 2394 | "1": "kiny", 2395 | "-1": "nyoro" 2396 | }, 2397 | "luy": { 2398 | "0": "Lero", 2399 | "1": "Mgamba", 2400 | "-1": "Mgorova" 2401 | }, 2402 | "luy_ke": { 2403 | "0": "Lero", 2404 | "1": "Mgamba", 2405 | "-1": "Mgorova" 2406 | }, 2407 | "lv": { 2408 | "0": "šodien", 2409 | "1": "rīt", 2410 | "-1": "vakar" 2411 | }, 2412 | "lv_lv": { 2413 | "0": "šodien", 2414 | "1": "rīt", 2415 | "-1": "vakar" 2416 | }, 2417 | "mas": { 2418 | "0": "Táatá", 2419 | "1": "Tááisérè", 2420 | "-1": "Ŋolé" 2421 | }, 2422 | "mas_ke": { 2423 | "0": "Táatá", 2424 | "1": "Tááisérè", 2425 | "-1": "Ŋolé" 2426 | }, 2427 | "mas_tz": { 2428 | "0": "Táatá", 2429 | "1": "Tááisérè", 2430 | "-1": "Ŋolé" 2431 | }, 2432 | "mer": { 2433 | "0": "Narua", 2434 | "1": "Rũjũ", 2435 | "-1": "Ĩgoro" 2436 | }, 2437 | "mer_ke": { 2438 | "0": "Narua", 2439 | "1": "Rũjũ", 2440 | "-1": "Ĩgoro" 2441 | }, 2442 | "mfe": { 2443 | "0": "Zordi", 2444 | "1": "Demin", 2445 | "-1": "Yer" 2446 | }, 2447 | "mfe_mu": { 2448 | "0": "Zordi", 2449 | "1": "Demin", 2450 | "-1": "Yer" 2451 | }, 2452 | "mg": { 2453 | "0": "Anio", 2454 | "1": "Rahampitso", 2455 | "-1": "Omaly" 2456 | }, 2457 | "mg_mg": { 2458 | "0": "Anio", 2459 | "1": "Rahampitso", 2460 | "-1": "Omaly" 2461 | }, 2462 | "mgh": { 2463 | "0": "lel’lo", 2464 | "1": "me’llo", 2465 | "-1": "n’chana" 2466 | }, 2467 | "mgh_mz": { 2468 | "0": "lel’lo", 2469 | "1": "me’llo", 2470 | "-1": "n’chana" 2471 | }, 2472 | "mgo": { 2473 | "0": "tèchɔ̀ŋ", 2474 | "1": "isu", 2475 | "-1": "ikwiri" 2476 | }, 2477 | "mgo_cm": { 2478 | "0": "tèchɔ̀ŋ", 2479 | "1": "isu", 2480 | "-1": "ikwiri" 2481 | }, 2482 | "mi": { 2483 | "0": "āianei", 2484 | "1": "āpōpō", 2485 | "-1": "inanahi" 2486 | }, 2487 | "mi_nz": { 2488 | "0": "āianei", 2489 | "1": "āpōpō", 2490 | "-1": "inanahi" 2491 | }, 2492 | "mk": { 2493 | "0": "денес", 2494 | "1": "утре", 2495 | "-1": "вчера" 2496 | }, 2497 | "mk_mk": { 2498 | "0": "денес", 2499 | "1": "утре", 2500 | "-1": "вчера" 2501 | }, 2502 | "ml": { 2503 | "0": "ഇന്ന്", 2504 | "1": "നാളെ", 2505 | "-1": "ഇന്നലെ" 2506 | }, 2507 | "ml_in": { 2508 | "0": "ഇന്ന്", 2509 | "1": "നാളെ", 2510 | "-1": "ഇന്നലെ" 2511 | }, 2512 | "mn": { 2513 | "0": "өнөөдөр", 2514 | "1": "маргааш", 2515 | "-1": "өчигдөр" 2516 | }, 2517 | "mn_mn": { 2518 | "0": "өнөөдөр", 2519 | "1": "маргааш", 2520 | "-1": "өчигдөр" 2521 | }, 2522 | "mr": { 2523 | "0": "आज", 2524 | "1": "उद्या", 2525 | "-1": "काल" 2526 | }, 2527 | "mr_in": { 2528 | "0": "आज", 2529 | "1": "उद्या", 2530 | "-1": "काल" 2531 | }, 2532 | "ms": { 2533 | "0": "hari ini", 2534 | "1": "esok", 2535 | "-1": "semalam" 2536 | }, 2537 | "ms_bn": { 2538 | "0": "hari ini", 2539 | "1": "esok", 2540 | "-1": "semalam" 2541 | }, 2542 | "ms_my": { 2543 | "0": "hari ini", 2544 | "1": "esok", 2545 | "-1": "semalam" 2546 | }, 2547 | "ms_sg": { 2548 | "0": "hari ini", 2549 | "1": "esok", 2550 | "-1": "semalam" 2551 | }, 2552 | "mt": { 2553 | "0": "illum", 2554 | "1": "għada", 2555 | "-1": "lbieraħ" 2556 | }, 2557 | "mt_mt": { 2558 | "0": "illum", 2559 | "1": "għada", 2560 | "-1": "lbieraħ" 2561 | }, 2562 | "mua": { 2563 | "0": "Tǝ’nahko", 2564 | "1": "Tǝ’nane", 2565 | "-1": "Tǝsoo" 2566 | }, 2567 | "mua_cm": { 2568 | "0": "Tǝ’nahko", 2569 | "1": "Tǝ’nane", 2570 | "-1": "Tǝsoo" 2571 | }, 2572 | "my": { 2573 | "0": "ယနေ့", 2574 | "1": "မနက်ဖြန်", 2575 | "-1": "မနေ့က" 2576 | }, 2577 | "my_mm": { 2578 | "0": "ယနေ့", 2579 | "1": "မနက်ဖြန်", 2580 | "-1": "မနေ့က" 2581 | }, 2582 | "mzn": { 2583 | "0": "اَمروز", 2584 | "1": "فِردا", 2585 | "-1": "دیروز" 2586 | }, 2587 | "mzn_ir": { 2588 | "0": "اَمروز", 2589 | "1": "فِردا", 2590 | "-1": "دیروز" 2591 | }, 2592 | "naq": { 2593 | "0": "Neetsee", 2594 | "1": "tomorrow", 2595 | "-1": "yesterday" 2596 | }, 2597 | "naq_na": { 2598 | "0": "Neetsee", 2599 | "1": "tomorrow", 2600 | "-1": "yesterday" 2601 | }, 2602 | "nb": { 2603 | "0": "i dag", 2604 | "1": "i morgen", 2605 | "-1": "i går" 2606 | }, 2607 | "nb_no": { 2608 | "0": "i dag", 2609 | "1": "i morgen", 2610 | "-1": "i går" 2611 | }, 2612 | "nb_sj": { 2613 | "0": "i dag", 2614 | "1": "i morgen", 2615 | "-1": "i går" 2616 | }, 2617 | "nd": { 2618 | "0": "Lamuhla", 2619 | "1": "Kusasa", 2620 | "-1": "Izolo" 2621 | }, 2622 | "nd_zw": { 2623 | "0": "Lamuhla", 2624 | "1": "Kusasa", 2625 | "-1": "Izolo" 2626 | }, 2627 | "nds": { 2628 | "0": "vundaag", 2629 | "1": "morgen", 2630 | "-1": "güstern" 2631 | }, 2632 | "nds_de": { 2633 | "0": "vundaag", 2634 | "1": "morgen", 2635 | "-1": "güstern" 2636 | }, 2637 | "nds_nl": { 2638 | "0": "vundaag", 2639 | "1": "morgen", 2640 | "-1": "güstern" 2641 | }, 2642 | "ne": { 2643 | "0": "आज", 2644 | "1": "भोलि", 2645 | "-1": "हिजो" 2646 | }, 2647 | "ne_in": { 2648 | "0": "आज", 2649 | "1": "भोलि", 2650 | "-1": "हिजो" 2651 | }, 2652 | "ne_np": { 2653 | "0": "आज", 2654 | "1": "भोलि", 2655 | "-1": "हिजो" 2656 | }, 2657 | "nl": { 2658 | "0": "vandaag", 2659 | "1": "morgen", 2660 | "-1": "gisteren" 2661 | }, 2662 | "nl_aw": { 2663 | "0": "vandaag", 2664 | "1": "morgen", 2665 | "-1": "gisteren" 2666 | }, 2667 | "nl_be": { 2668 | "0": "vandaag", 2669 | "1": "morgen", 2670 | "-1": "gisteren" 2671 | }, 2672 | "nl_bq": { 2673 | "0": "vandaag", 2674 | "1": "morgen", 2675 | "-1": "gisteren" 2676 | }, 2677 | "nl_cw": { 2678 | "0": "vandaag", 2679 | "1": "morgen", 2680 | "-1": "gisteren" 2681 | }, 2682 | "nl_nl": { 2683 | "0": "vandaag", 2684 | "1": "morgen", 2685 | "-1": "gisteren" 2686 | }, 2687 | "nl_sr": { 2688 | "0": "vandaag", 2689 | "1": "morgen", 2690 | "-1": "gisteren" 2691 | }, 2692 | "nl_sx": { 2693 | "0": "vandaag", 2694 | "1": "morgen", 2695 | "-1": "gisteren" 2696 | }, 2697 | "nmg": { 2698 | "0": "Dɔl", 2699 | "1": "Namáná", 2700 | "-1": "Nakugú" 2701 | }, 2702 | "nmg_cm": { 2703 | "0": "Dɔl", 2704 | "1": "Namáná", 2705 | "-1": "Nakugú" 2706 | }, 2707 | "nn": { 2708 | "0": "i dag", 2709 | "1": "i morgon", 2710 | "-1": "i går" 2711 | }, 2712 | "nn_no": { 2713 | "0": "i dag", 2714 | "1": "i morgon", 2715 | "-1": "i går" 2716 | }, 2717 | "nnh": { 2718 | "0": "lyɛ̌ʼɔɔn", 2719 | "1": "jǔɔ gẅie à ne ntóo", 2720 | "-1": "jǔɔ gẅie à ka tɔ̌g" 2721 | }, 2722 | "nnh_cm": { 2723 | "0": "lyɛ̌ʼɔɔn", 2724 | "1": "jǔɔ gẅie à ne ntóo", 2725 | "-1": "jǔɔ gẅie à ka tɔ̌g" 2726 | }, 2727 | "nus": { 2728 | "0": "Walɛ", 2729 | "1": "Ruun", 2730 | "-1": "Pan" 2731 | }, 2732 | "nus_ss": { 2733 | "0": "Walɛ", 2734 | "1": "Ruun", 2735 | "-1": "Pan" 2736 | }, 2737 | "nyn": { 2738 | "0": "Erizooba", 2739 | "1": "Nyenkyakare", 2740 | "-1": "Nyomwabazyo" 2741 | }, 2742 | "nyn_ug": { 2743 | "0": "Erizooba", 2744 | "1": "Nyenkyakare", 2745 | "-1": "Nyomwabazyo" 2746 | }, 2747 | "om": { 2748 | "0": "today", 2749 | "1": "tomorrow", 2750 | "-1": "yesterday" 2751 | }, 2752 | "om_et": { 2753 | "0": "today", 2754 | "1": "tomorrow", 2755 | "-1": "yesterday" 2756 | }, 2757 | "om_ke": { 2758 | "0": "today", 2759 | "1": "tomorrow", 2760 | "-1": "yesterday" 2761 | }, 2762 | "or": { 2763 | "0": "ଆଜି", 2764 | "1": "ଆସନ୍ତାକାଲି", 2765 | "-1": "ଗତକାଲି" 2766 | }, 2767 | "or_in": { 2768 | "0": "ଆଜି", 2769 | "1": "ଆସନ୍ତାକାଲି", 2770 | "-1": "ଗତକାଲି" 2771 | }, 2772 | "os": { 2773 | "0": "Абон", 2774 | "1": "Сом", 2775 | "-1": "Знон" 2776 | }, 2777 | "os_ge": { 2778 | "0": "Абон", 2779 | "1": "Сом", 2780 | "-1": "Знон" 2781 | }, 2782 | "os_ru": { 2783 | "0": "Абон", 2784 | "1": "Сом", 2785 | "-1": "Знон" 2786 | }, 2787 | "pa": { 2788 | "0": "ਅੱਜ", 2789 | "1": "ਭਲਕੇ", 2790 | "-1": "ਬੀਤਿਆ ਕੱਲ੍ਹ" 2791 | }, 2792 | "pa_arab": { 2793 | "0": "today", 2794 | "1": "tomorrow", 2795 | "-1": "yesterday" 2796 | }, 2797 | "pa_arab_pk": { 2798 | "0": "today", 2799 | "1": "tomorrow", 2800 | "-1": "yesterday" 2801 | }, 2802 | "pa_guru": { 2803 | "0": "ਅੱਜ", 2804 | "1": "ਭਲਕੇ", 2805 | "-1": "ਬੀਤਿਆ ਕੱਲ੍ਹ" 2806 | }, 2807 | "pa_guru_in": { 2808 | "0": "ਅੱਜ", 2809 | "1": "ਭਲਕੇ", 2810 | "-1": "ਬੀਤਿਆ ਕੱਲ੍ਹ" 2811 | }, 2812 | "pl": { 2813 | "0": "dzisiaj", 2814 | "1": "jutro", 2815 | "-1": "wczoraj" 2816 | }, 2817 | "pl_pl": { 2818 | "0": "dzisiaj", 2819 | "1": "jutro", 2820 | "-1": "wczoraj" 2821 | }, 2822 | "prg": { 2823 | "0": "šandēinan", 2824 | "1": "ankstāinan", 2825 | "-1": "bītan" 2826 | }, 2827 | "prg_001": { 2828 | "0": "šandēinan", 2829 | "1": "ankstāinan", 2830 | "-1": "bītan" 2831 | }, 2832 | "ps": { 2833 | "0": "today", 2834 | "1": "tomorrow", 2835 | "-1": "yesterday" 2836 | }, 2837 | "ps_af": { 2838 | "0": "today", 2839 | "1": "tomorrow", 2840 | "-1": "yesterday" 2841 | }, 2842 | "ps_pk": { 2843 | "0": "today", 2844 | "1": "tomorrow", 2845 | "-1": "yesterday" 2846 | }, 2847 | "pt": { 2848 | "0": "hoje", 2849 | "1": "amanhã", 2850 | "-1": "ontem" 2851 | }, 2852 | "pt_ao": { 2853 | "0": "hoje", 2854 | "1": "amanhã", 2855 | "-1": "ontem" 2856 | }, 2857 | "pt_br": { 2858 | "0": "hoje", 2859 | "1": "amanhã", 2860 | "-1": "ontem" 2861 | }, 2862 | "pt_ch": { 2863 | "0": "hoje", 2864 | "1": "amanhã", 2865 | "-1": "ontem" 2866 | }, 2867 | "pt_cv": { 2868 | "0": "hoje", 2869 | "1": "amanhã", 2870 | "-1": "ontem" 2871 | }, 2872 | "pt_gq": { 2873 | "0": "hoje", 2874 | "1": "amanhã", 2875 | "-1": "ontem" 2876 | }, 2877 | "pt_gw": { 2878 | "0": "hoje", 2879 | "1": "amanhã", 2880 | "-1": "ontem" 2881 | }, 2882 | "pt_lu": { 2883 | "0": "hoje", 2884 | "1": "amanhã", 2885 | "-1": "ontem" 2886 | }, 2887 | "pt_mo": { 2888 | "0": "hoje", 2889 | "1": "amanhã", 2890 | "-1": "ontem" 2891 | }, 2892 | "pt_mz": { 2893 | "0": "hoje", 2894 | "1": "amanhã", 2895 | "-1": "ontem" 2896 | }, 2897 | "pt_pt": { 2898 | "0": "hoje", 2899 | "1": "amanhã", 2900 | "-1": "ontem" 2901 | }, 2902 | "pt_st": { 2903 | "0": "hoje", 2904 | "1": "amanhã", 2905 | "-1": "ontem" 2906 | }, 2907 | "pt_tl": { 2908 | "0": "hoje", 2909 | "1": "amanhã", 2910 | "-1": "ontem" 2911 | }, 2912 | "qu": { 2913 | "0": "kunan punchaw", 2914 | "1": "paqarin", 2915 | "-1": "qayna punchaw" 2916 | }, 2917 | "qu_bo": { 2918 | "0": "kunan punchaw", 2919 | "1": "paqarin", 2920 | "-1": "qayna punchaw" 2921 | }, 2922 | "qu_ec": { 2923 | "0": "kunan punchaw", 2924 | "1": "paqarin", 2925 | "-1": "qayna punchaw" 2926 | }, 2927 | "qu_pe": { 2928 | "0": "kunan punchaw", 2929 | "1": "paqarin", 2930 | "-1": "qayna punchaw" 2931 | }, 2932 | "rm": { 2933 | "0": "oz", 2934 | "1": "damaun", 2935 | "-1": "ier" 2936 | }, 2937 | "rm_ch": { 2938 | "0": "oz", 2939 | "1": "damaun", 2940 | "-1": "ier" 2941 | }, 2942 | "rn": { 2943 | "0": "Uyu musi", 2944 | "1": "Ejo (hazoza)", 2945 | "-1": "Ejo (haheze)" 2946 | }, 2947 | "rn_bi": { 2948 | "0": "Uyu musi", 2949 | "1": "Ejo (hazoza)", 2950 | "-1": "Ejo (haheze)" 2951 | }, 2952 | "ro": { 2953 | "0": "azi", 2954 | "1": "mâine", 2955 | "-1": "ieri" 2956 | }, 2957 | "ro_md": { 2958 | "0": "azi", 2959 | "1": "mâine", 2960 | "-1": "ieri" 2961 | }, 2962 | "ro_ro": { 2963 | "0": "azi", 2964 | "1": "mâine", 2965 | "-1": "ieri" 2966 | }, 2967 | "rof": { 2968 | "0": "Linu", 2969 | "1": "Ng’ama", 2970 | "-1": "Hiyo" 2971 | }, 2972 | "rof_tz": { 2973 | "0": "Linu", 2974 | "1": "Ng’ama", 2975 | "-1": "Hiyo" 2976 | }, 2977 | "root": { 2978 | "0": "today", 2979 | "1": "tomorrow", 2980 | "-1": "yesterday" 2981 | }, 2982 | "ru": { 2983 | "0": "сегодня", 2984 | "1": "завтра", 2985 | "-1": "вчера" 2986 | }, 2987 | "ru_by": { 2988 | "0": "сегодня", 2989 | "1": "завтра", 2990 | "-1": "вчера" 2991 | }, 2992 | "ru_kg": { 2993 | "0": "сегодня", 2994 | "1": "завтра", 2995 | "-1": "вчера" 2996 | }, 2997 | "ru_kz": { 2998 | "0": "сегодня", 2999 | "1": "завтра", 3000 | "-1": "вчера" 3001 | }, 3002 | "ru_md": { 3003 | "0": "сегодня", 3004 | "1": "завтра", 3005 | "-1": "вчера" 3006 | }, 3007 | "ru_ru": { 3008 | "0": "сегодня", 3009 | "1": "завтра", 3010 | "-1": "вчера" 3011 | }, 3012 | "ru_ua": { 3013 | "0": "сегодня", 3014 | "1": "завтра", 3015 | "-1": "вчера" 3016 | }, 3017 | "rw": { 3018 | "0": "today", 3019 | "1": "tomorrow", 3020 | "-1": "yesterday" 3021 | }, 3022 | "rw_rw": { 3023 | "0": "today", 3024 | "1": "tomorrow", 3025 | "-1": "yesterday" 3026 | }, 3027 | "rwk": { 3028 | "0": "Inu", 3029 | "1": "Ngama", 3030 | "-1": "Ukou" 3031 | }, 3032 | "rwk_tz": { 3033 | "0": "Inu", 3034 | "1": "Ngama", 3035 | "-1": "Ukou" 3036 | }, 3037 | "sah": { 3038 | "0": "Бүгүн", 3039 | "1": "Сарсын", 3040 | "-1": "Бэҕэһээ" 3041 | }, 3042 | "sah_ru": { 3043 | "0": "Бүгүн", 3044 | "1": "Сарсын", 3045 | "-1": "Бэҕэһээ" 3046 | }, 3047 | "saq": { 3048 | "0": "Duo", 3049 | "1": "Taisere", 3050 | "-1": "Ng’ole" 3051 | }, 3052 | "saq_ke": { 3053 | "0": "Duo", 3054 | "1": "Taisere", 3055 | "-1": "Ng’ole" 3056 | }, 3057 | "sbp": { 3058 | "0": "Ineng’uni", 3059 | "1": "Pamulaawu", 3060 | "-1": "Imehe" 3061 | }, 3062 | "sbp_tz": { 3063 | "0": "Ineng’uni", 3064 | "1": "Pamulaawu", 3065 | "-1": "Imehe" 3066 | }, 3067 | "sd": { 3068 | "0": "اڄ", 3069 | "1": "سڀاڻي", 3070 | "-1": "ڪل" 3071 | }, 3072 | "sd_pk": { 3073 | "0": "اڄ", 3074 | "1": "سڀاڻي", 3075 | "-1": "ڪل" 3076 | }, 3077 | "se": { 3078 | "0": "odne", 3079 | "1": "ihttin", 3080 | "-1": "ikte" 3081 | }, 3082 | "se_fi": { 3083 | "0": "odne", 3084 | "1": "ihttin", 3085 | "-1": "ikte" 3086 | }, 3087 | "se_no": { 3088 | "0": "odne", 3089 | "1": "ihttin", 3090 | "-1": "ikte" 3091 | }, 3092 | "se_se": { 3093 | "0": "odne", 3094 | "1": "ihttin", 3095 | "-1": "ikte" 3096 | }, 3097 | "seh": { 3098 | "0": "Lero", 3099 | "1": "Manguana", 3100 | "-1": "Zuro" 3101 | }, 3102 | "seh_mz": { 3103 | "0": "Lero", 3104 | "1": "Manguana", 3105 | "-1": "Zuro" 3106 | }, 3107 | "ses": { 3108 | "0": "Hõo", 3109 | "1": "Suba", 3110 | "-1": "Bi" 3111 | }, 3112 | "ses_ml": { 3113 | "0": "Hõo", 3114 | "1": "Suba", 3115 | "-1": "Bi" 3116 | }, 3117 | "sg": { 3118 | "0": "Lâsô", 3119 | "1": "Kêkerêke", 3120 | "-1": "Bîrï" 3121 | }, 3122 | "sg_cf": { 3123 | "0": "Lâsô", 3124 | "1": "Kêkerêke", 3125 | "-1": "Bîrï" 3126 | }, 3127 | "shi": { 3128 | "0": "ⴰⵙⵙⴰ", 3129 | "1": "ⴰⵙⴽⴽⴰ", 3130 | "-1": "ⵉⴹⵍⵍⵉ" 3131 | }, 3132 | "shi_latn": { 3133 | "0": "assa", 3134 | "1": "askka", 3135 | "-1": "iḍlli" 3136 | }, 3137 | "shi_latn_ma": { 3138 | "0": "assa", 3139 | "1": "askka", 3140 | "-1": "iḍlli" 3141 | }, 3142 | "shi_tfng": { 3143 | "0": "ⴰⵙⵙⴰ", 3144 | "1": "ⴰⵙⴽⴽⴰ", 3145 | "-1": "ⵉⴹⵍⵍⵉ" 3146 | }, 3147 | "shi_tfng_ma": { 3148 | "0": "ⴰⵙⵙⴰ", 3149 | "1": "ⴰⵙⴽⴽⴰ", 3150 | "-1": "ⵉⴹⵍⵍⵉ" 3151 | }, 3152 | "si": { 3153 | "0": "අද", 3154 | "1": "හෙට", 3155 | "-1": "ඊයේ" 3156 | }, 3157 | "si_lk": { 3158 | "0": "අද", 3159 | "1": "හෙට", 3160 | "-1": "ඊයේ" 3161 | }, 3162 | "sk": { 3163 | "0": "dnes", 3164 | "1": "zajtra", 3165 | "-1": "včera" 3166 | }, 3167 | "sk_sk": { 3168 | "0": "dnes", 3169 | "1": "zajtra", 3170 | "-1": "včera" 3171 | }, 3172 | "sl": { 3173 | "0": "danes", 3174 | "1": "jutri", 3175 | "-1": "včeraj" 3176 | }, 3177 | "sl_si": { 3178 | "0": "danes", 3179 | "1": "jutri", 3180 | "-1": "včeraj" 3181 | }, 3182 | "smn": { 3183 | "0": "today", 3184 | "1": "tomorrow", 3185 | "-1": "yesterday" 3186 | }, 3187 | "smn_fi": { 3188 | "0": "today", 3189 | "1": "tomorrow", 3190 | "-1": "yesterday" 3191 | }, 3192 | "sn": { 3193 | "0": "Nhasi", 3194 | "1": "Mangwana", 3195 | "-1": "Nezuro" 3196 | }, 3197 | "sn_zw": { 3198 | "0": "Nhasi", 3199 | "1": "Mangwana", 3200 | "-1": "Nezuro" 3201 | }, 3202 | "so": { 3203 | "0": "Maanta", 3204 | "1": "Berri", 3205 | "-1": "Shalay" 3206 | }, 3207 | "so_dj": { 3208 | "0": "Maanta", 3209 | "1": "Berri", 3210 | "-1": "Shalay" 3211 | }, 3212 | "so_et": { 3213 | "0": "Maanta", 3214 | "1": "Berri", 3215 | "-1": "Shalay" 3216 | }, 3217 | "so_ke": { 3218 | "0": "Maanta", 3219 | "1": "Berri", 3220 | "-1": "Shalay" 3221 | }, 3222 | "so_so": { 3223 | "0": "Maanta", 3224 | "1": "Berri", 3225 | "-1": "Shalay" 3226 | }, 3227 | "sq": { 3228 | "0": "sot", 3229 | "1": "nesër", 3230 | "-1": "dje" 3231 | }, 3232 | "sq_al": { 3233 | "0": "sot", 3234 | "1": "nesër", 3235 | "-1": "dje" 3236 | }, 3237 | "sq_mk": { 3238 | "0": "sot", 3239 | "1": "nesër", 3240 | "-1": "dje" 3241 | }, 3242 | "sq_xk": { 3243 | "0": "sot", 3244 | "1": "nesër", 3245 | "-1": "dje" 3246 | }, 3247 | "sr": { 3248 | "0": "данас", 3249 | "1": "сутра", 3250 | "-1": "јуче" 3251 | }, 3252 | "sr_cyrl": { 3253 | "0": "данас", 3254 | "1": "сутра", 3255 | "-1": "јуче" 3256 | }, 3257 | "sr_cyrl_ba": { 3258 | "0": "данас", 3259 | "1": "сутра", 3260 | "-1": "јуче" 3261 | }, 3262 | "sr_cyrl_me": { 3263 | "0": "данас", 3264 | "1": "сутра", 3265 | "-1": "јуче" 3266 | }, 3267 | "sr_cyrl_rs": { 3268 | "0": "данас", 3269 | "1": "сутра", 3270 | "-1": "јуче" 3271 | }, 3272 | "sr_cyrl_xk": { 3273 | "0": "данас", 3274 | "1": "сутра", 3275 | "-1": "јуче" 3276 | }, 3277 | "sr_latn": { 3278 | "0": "danas", 3279 | "1": "sutra", 3280 | "-1": "juče" 3281 | }, 3282 | "sr_latn_ba": { 3283 | "0": "danas", 3284 | "1": "sutra", 3285 | "-1": "juče" 3286 | }, 3287 | "sr_latn_me": { 3288 | "0": "danas", 3289 | "1": "sutra", 3290 | "-1": "juče" 3291 | }, 3292 | "sr_latn_rs": { 3293 | "0": "danas", 3294 | "1": "sutra", 3295 | "-1": "juče" 3296 | }, 3297 | "sr_latn_xk": { 3298 | "0": "danas", 3299 | "1": "sutra", 3300 | "-1": "juče" 3301 | }, 3302 | "sv": { 3303 | "0": "i dag", 3304 | "1": "i morgon", 3305 | "-1": "i går" 3306 | }, 3307 | "sv_ax": { 3308 | "0": "i dag", 3309 | "1": "i morgon", 3310 | "-1": "i går" 3311 | }, 3312 | "sv_fi": { 3313 | "0": "i dag", 3314 | "1": "i morgon", 3315 | "-1": "i går" 3316 | }, 3317 | "sv_se": { 3318 | "0": "i dag", 3319 | "1": "i morgon", 3320 | "-1": "i går" 3321 | }, 3322 | "sw": { 3323 | "0": "leo", 3324 | "1": "kesho", 3325 | "-1": "jana" 3326 | }, 3327 | "sw_cd": { 3328 | "0": "leo", 3329 | "1": "kesho", 3330 | "-1": "jana" 3331 | }, 3332 | "sw_ke": { 3333 | "0": "leo", 3334 | "1": "kesho", 3335 | "-1": "jana" 3336 | }, 3337 | "sw_tz": { 3338 | "0": "leo", 3339 | "1": "kesho", 3340 | "-1": "jana" 3341 | }, 3342 | "sw_ug": { 3343 | "0": "leo", 3344 | "1": "kesho", 3345 | "-1": "jana" 3346 | }, 3347 | "ta": { 3348 | "0": "இன்று", 3349 | "1": "நாளை", 3350 | "-1": "நேற்று" 3351 | }, 3352 | "ta_in": { 3353 | "0": "இன்று", 3354 | "1": "நாளை", 3355 | "-1": "நேற்று" 3356 | }, 3357 | "ta_lk": { 3358 | "0": "இன்று", 3359 | "1": "நாளை", 3360 | "-1": "நேற்று" 3361 | }, 3362 | "ta_my": { 3363 | "0": "இன்று", 3364 | "1": "நாளை", 3365 | "-1": "நேற்று" 3366 | }, 3367 | "ta_sg": { 3368 | "0": "இன்று", 3369 | "1": "நாளை", 3370 | "-1": "நேற்று" 3371 | }, 3372 | "te": { 3373 | "0": "ఈ రోజు", 3374 | "1": "రేపు", 3375 | "-1": "నిన్న" 3376 | }, 3377 | "te_in": { 3378 | "0": "ఈ రోజు", 3379 | "1": "రేపు", 3380 | "-1": "నిన్న" 3381 | }, 3382 | "teo": { 3383 | "0": "Lolo", 3384 | "1": "Moi", 3385 | "-1": "Jaan" 3386 | }, 3387 | "teo_ke": { 3388 | "0": "Lolo", 3389 | "1": "Moi", 3390 | "-1": "Jaan" 3391 | }, 3392 | "teo_ug": { 3393 | "0": "Lolo", 3394 | "1": "Moi", 3395 | "-1": "Jaan" 3396 | }, 3397 | "tg": { 3398 | "0": "имрӯз", 3399 | "1": "фардо", 3400 | "-1": "дирӯз" 3401 | }, 3402 | "tg_tj": { 3403 | "0": "имрӯз", 3404 | "1": "фардо", 3405 | "-1": "дирӯз" 3406 | }, 3407 | "th": { 3408 | "0": "วันนี้", 3409 | "1": "พรุ่งนี้", 3410 | "-1": "เมื่อวาน" 3411 | }, 3412 | "th_th": { 3413 | "0": "วันนี้", 3414 | "1": "พรุ่งนี้", 3415 | "-1": "เมื่อวาน" 3416 | }, 3417 | "ti": { 3418 | "0": "ሎሚ", 3419 | "1": "ጽባሕ", 3420 | "-1": "ትማሊ" 3421 | }, 3422 | "ti_er": { 3423 | "0": "ሎሚ", 3424 | "1": "ጽባሕ", 3425 | "-1": "ትማሊ" 3426 | }, 3427 | "ti_et": { 3428 | "0": "ሎሚ", 3429 | "1": "ጽባሕ", 3430 | "-1": "ትማሊ" 3431 | }, 3432 | "tk": { 3433 | "0": "şu gün", 3434 | "1": "ertir", 3435 | "-1": "düýn" 3436 | }, 3437 | "tk_tm": { 3438 | "0": "şu gün", 3439 | "1": "ertir", 3440 | "-1": "düýn" 3441 | }, 3442 | "to": { 3443 | "0": "ʻahó ni", 3444 | "1": "ʻapongipongi", 3445 | "-1": "ʻaneafi" 3446 | }, 3447 | "to_to": { 3448 | "0": "ʻahó ni", 3449 | "1": "ʻapongipongi", 3450 | "-1": "ʻaneafi" 3451 | }, 3452 | "tr": { 3453 | "0": "bugün", 3454 | "1": "yarın", 3455 | "-1": "dün" 3456 | }, 3457 | "tr_cy": { 3458 | "0": "bugün", 3459 | "1": "yarın", 3460 | "-1": "dün" 3461 | }, 3462 | "tr_tr": { 3463 | "0": "bugün", 3464 | "1": "yarın", 3465 | "-1": "dün" 3466 | }, 3467 | "tt": { 3468 | "0": "бүген", 3469 | "1": "иртәгә", 3470 | "-1": "кичә" 3471 | }, 3472 | "tt_ru": { 3473 | "0": "бүген", 3474 | "1": "иртәгә", 3475 | "-1": "кичә" 3476 | }, 3477 | "twq": { 3478 | "0": "Hõo", 3479 | "1": "Suba", 3480 | "-1": "Bi" 3481 | }, 3482 | "twq_ne": { 3483 | "0": "Hõo", 3484 | "1": "Suba", 3485 | "-1": "Bi" 3486 | }, 3487 | "tzm": { 3488 | "0": "Assa", 3489 | "1": "Asekka", 3490 | "-1": "Assenaṭ" 3491 | }, 3492 | "tzm_ma": { 3493 | "0": "Assa", 3494 | "1": "Asekka", 3495 | "-1": "Assenaṭ" 3496 | }, 3497 | "ug": { 3498 | "0": "بۈگۈن", 3499 | "1": "ئەتە", 3500 | "-1": "تۈنۈگۈن" 3501 | }, 3502 | "ug_cn": { 3503 | "0": "بۈگۈن", 3504 | "1": "ئەتە", 3505 | "-1": "تۈنۈگۈن" 3506 | }, 3507 | "uk": { 3508 | "0": "сьогодні", 3509 | "1": "завтра", 3510 | "-1": "учора" 3511 | }, 3512 | "uk_ua": { 3513 | "0": "сьогодні", 3514 | "1": "завтра", 3515 | "-1": "учора" 3516 | }, 3517 | "ur": { 3518 | "0": "آج", 3519 | "1": "آئندہ کل", 3520 | "-1": "گزشتہ کل" 3521 | }, 3522 | "ur_in": { 3523 | "0": "آج", 3524 | "1": "آئندہ کل", 3525 | "-1": "گزشتہ کل" 3526 | }, 3527 | "ur_pk": { 3528 | "0": "آج", 3529 | "1": "آئندہ کل", 3530 | "-1": "گزشتہ کل" 3531 | }, 3532 | "uz": { 3533 | "0": "bugun", 3534 | "1": "ertaga", 3535 | "-1": "kecha" 3536 | }, 3537 | "uz_arab": { 3538 | "0": "today", 3539 | "1": "tomorrow", 3540 | "-1": "yesterday" 3541 | }, 3542 | "uz_arab_af": { 3543 | "0": "today", 3544 | "1": "tomorrow", 3545 | "-1": "yesterday" 3546 | }, 3547 | "uz_cyrl": { 3548 | "0": "бугун", 3549 | "1": "эртага", 3550 | "-1": "кеча" 3551 | }, 3552 | "uz_cyrl_uz": { 3553 | "0": "бугун", 3554 | "1": "эртага", 3555 | "-1": "кеча" 3556 | }, 3557 | "uz_latn": { 3558 | "0": "bugun", 3559 | "1": "ertaga", 3560 | "-1": "kecha" 3561 | }, 3562 | "uz_latn_uz": { 3563 | "0": "bugun", 3564 | "1": "ertaga", 3565 | "-1": "kecha" 3566 | }, 3567 | "vai": { 3568 | "0": "ꗦꗷ", 3569 | "1": "ꔻꕯ", 3570 | "-1": "ꖴꖸ" 3571 | }, 3572 | "vai_latn": { 3573 | "0": "wɛlɛ", 3574 | "1": "sina", 3575 | "-1": "kunu" 3576 | }, 3577 | "vai_latn_lr": { 3578 | "0": "wɛlɛ", 3579 | "1": "sina", 3580 | "-1": "kunu" 3581 | }, 3582 | "vai_vaii": { 3583 | "0": "ꗦꗷ", 3584 | "1": "ꔻꕯ", 3585 | "-1": "ꖴꖸ" 3586 | }, 3587 | "vai_vaii_lr": { 3588 | "0": "ꗦꗷ", 3589 | "1": "ꔻꕯ", 3590 | "-1": "ꖴꖸ" 3591 | }, 3592 | "vi": { 3593 | "0": "Hôm nay", 3594 | "1": "Ngày mai", 3595 | "-1": "Hôm qua" 3596 | }, 3597 | "vi_vn": { 3598 | "0": "Hôm nay", 3599 | "1": "Ngày mai", 3600 | "-1": "Hôm qua" 3601 | }, 3602 | "vo": { 3603 | "0": "adelo", 3604 | "1": "odelo", 3605 | "-1": "ädelo" 3606 | }, 3607 | "vo_001": { 3608 | "0": "adelo", 3609 | "1": "odelo", 3610 | "-1": "ädelo" 3611 | }, 3612 | "vun": { 3613 | "0": "Inu", 3614 | "1": "Ngama", 3615 | "-1": "Ukou" 3616 | }, 3617 | "vun_tz": { 3618 | "0": "Inu", 3619 | "1": "Ngama", 3620 | "-1": "Ukou" 3621 | }, 3622 | "wae": { 3623 | "0": "Hitte", 3624 | "1": "Móre", 3625 | "-1": "Gešter" 3626 | }, 3627 | "wae_ch": { 3628 | "0": "Hitte", 3629 | "1": "Móre", 3630 | "-1": "Gešter" 3631 | }, 3632 | "wo": { 3633 | "0": "tay", 3634 | "1": "suba", 3635 | "-1": "démb" 3636 | }, 3637 | "wo_sn": { 3638 | "0": "tay", 3639 | "1": "suba", 3640 | "-1": "démb" 3641 | }, 3642 | "xh": { 3643 | "0": "today", 3644 | "1": "tomorrow", 3645 | "-1": "yesterday" 3646 | }, 3647 | "xh_za": { 3648 | "0": "today", 3649 | "1": "tomorrow", 3650 | "-1": "yesterday" 3651 | }, 3652 | "xog": { 3653 | "0": "Olwaleelo (leelo)", 3654 | "1": "Enkyo", 3655 | "-1": "Edho" 3656 | }, 3657 | "xog_ug": { 3658 | "0": "Olwaleelo (leelo)", 3659 | "1": "Enkyo", 3660 | "-1": "Edho" 3661 | }, 3662 | "yav": { 3663 | "0": "ínaan", 3664 | "1": "nakinyám", 3665 | "-1": "púyoó" 3666 | }, 3667 | "yav_cm": { 3668 | "0": "ínaan", 3669 | "1": "nakinyám", 3670 | "-1": "púyoó" 3671 | }, 3672 | "yi": { 3673 | "0": "היינט", 3674 | "1": "מארגן", 3675 | "-1": "נעכטן" 3676 | }, 3677 | "yi_001": { 3678 | "0": "היינט", 3679 | "1": "מארגן", 3680 | "-1": "נעכטן" 3681 | }, 3682 | "yo": { 3683 | "0": "Òní", 3684 | "1": "Ọ̀la", 3685 | "-1": "Àná" 3686 | }, 3687 | "yo_bj": { 3688 | "0": "Òní", 3689 | "1": "Ɔ̀la", 3690 | "-1": "Àná" 3691 | }, 3692 | "yo_ng": { 3693 | "0": "Òní", 3694 | "1": "Ọ̀la", 3695 | "-1": "Àná" 3696 | }, 3697 | "yue": { 3698 | "0": "今日", 3699 | "1": "聽日", 3700 | "-1": "尋日" 3701 | }, 3702 | "yue_hans": { 3703 | "0": "今日", 3704 | "1": "听日", 3705 | "-1": "寻日" 3706 | }, 3707 | "yue_hans_cn": { 3708 | "0": "今日", 3709 | "1": "听日", 3710 | "-1": "寻日" 3711 | }, 3712 | "yue_hant": { 3713 | "0": "今日", 3714 | "1": "聽日", 3715 | "-1": "尋日" 3716 | }, 3717 | "yue_hant_hk": { 3718 | "0": "今日", 3719 | "1": "聽日", 3720 | "-1": "尋日" 3721 | }, 3722 | "zgh": { 3723 | "0": "ⴰⵙⵙⴰ", 3724 | "1": "ⴰⵙⴽⴽⴰ", 3725 | "-1": "ⵉⴹⵍⵍⵉ" 3726 | }, 3727 | "zgh_ma": { 3728 | "0": "ⴰⵙⵙⴰ", 3729 | "1": "ⴰⵙⴽⴽⴰ", 3730 | "-1": "ⵉⴹⵍⵍⵉ" 3731 | }, 3732 | "zh": { 3733 | "0": "今天", 3734 | "1": "明天", 3735 | "-1": "昨天" 3736 | }, 3737 | "zh_hans": { 3738 | "0": "今天", 3739 | "1": "明天", 3740 | "-1": "昨天" 3741 | }, 3742 | "zh_hans_cn": { 3743 | "0": "今天", 3744 | "1": "明天", 3745 | "-1": "昨天" 3746 | }, 3747 | "zh_hans_hk": { 3748 | "0": "今天", 3749 | "1": "明天", 3750 | "-1": "昨天" 3751 | }, 3752 | "zh_hans_mo": { 3753 | "0": "今天", 3754 | "1": "明天", 3755 | "-1": "昨天" 3756 | }, 3757 | "zh_hans_sg": { 3758 | "0": "今天", 3759 | "1": "明天", 3760 | "-1": "昨天" 3761 | }, 3762 | "zh_hant": { 3763 | "0": "今天", 3764 | "1": "明天", 3765 | "-1": "昨天" 3766 | }, 3767 | "zh_hant_hk": { 3768 | "0": "今日", 3769 | "1": "明日", 3770 | "-1": "昨日" 3771 | }, 3772 | "zh_hant_mo": { 3773 | "0": "今日", 3774 | "1": "明日", 3775 | "-1": "昨日" 3776 | }, 3777 | "zh_hant_tw": { 3778 | "0": "今天", 3779 | "1": "明天", 3780 | "-1": "昨天" 3781 | }, 3782 | "zu": { 3783 | "0": "namhlanje", 3784 | "1": "kusasa", 3785 | "-1": "izolo" 3786 | }, 3787 | "zu_za": { 3788 | "0": "namhlanje", 3789 | "1": "kusasa", 3790 | "-1": "izolo" 3791 | } 3792 | } 3793 | -------------------------------------------------------------------------------- /src/extractDate.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | /* eslint-disable no-continue, no-negated-condition, import/no-namespace */ 4 | 5 | import { 6 | format as formatDate, 7 | parse as parseDate, 8 | isValid as isValidDate, 9 | } from 'date-fns'; 10 | import * as locales from 'date-fns/locale'; 11 | import moment from 'moment-timezone'; 12 | import dictionary from 'relative-date-names'; 13 | import createMovingChunks from './createMovingChunks'; 14 | import extractRelativeDate from './extractRelativeDate'; 15 | import createFormats from './createFormats'; 16 | import normalizeInput from './normalizeInput'; 17 | import Logger from './Logger'; 18 | import type { 19 | ConfigurationType, 20 | DateMatchType, 21 | UserConfigurationType, 22 | } from './types'; 23 | 24 | const log = Logger.child({ 25 | namespace: 'extractDate', 26 | }); 27 | 28 | const defaultConfiguration = { 29 | maximumAge: Infinity, 30 | minimumAge: Infinity, 31 | }; 32 | 33 | const formats = createFormats(); 34 | 35 | const dateFnsLocaleMap = { 36 | en: 'enUS', 37 | }; 38 | 39 | // eslint-disable-next-line complexity 40 | export default (input: string, userConfiguration: UserConfigurationType = defaultConfiguration): $ReadOnlyArray => { 41 | log.debug('attempting to extract date from "%s" input', input); 42 | 43 | const normalizedInput = normalizeInput(input); 44 | 45 | log.debug('normalized input to "%s"', normalizedInput); 46 | 47 | const configuration: ConfigurationType = { 48 | ...defaultConfiguration, 49 | ...userConfiguration, 50 | }; 51 | 52 | const locale = configuration.locale || 'en'; 53 | 54 | const dateFnsLocale = locales[dateFnsLocaleMap[locale] || locale]; 55 | 56 | if (!dateFnsLocale) { 57 | throw new Error('No translation available for the target locale (date-fns).'); 58 | } 59 | 60 | if (!dictionary[locale]) { 61 | throw new Error('No translation available for the target locale (relative dates).'); 62 | } 63 | 64 | if (configuration.timezone && !moment.tz.zone(configuration.timezone)) { 65 | throw new Error('Unrecognized timezone.'); 66 | } 67 | 68 | if (configuration.maximumAge && configuration.maximumAge < 0) { 69 | throw new Error('`maximumAge` must be a positive number.'); 70 | } 71 | 72 | if (configuration.minimumAge && configuration.minimumAge < 0) { 73 | throw new Error('`minimumAge` must be a positive number.'); 74 | } 75 | 76 | log.debug({ 77 | configuration, 78 | }, 'attempting to extract date from "%s" input', normalizedInput); 79 | 80 | let words = normalizedInput.split(' '); 81 | 82 | const matches = []; 83 | 84 | const baseDate = parseDate('12:00', 'HH:mm', new Date()); 85 | 86 | for (const format of formats) { 87 | const movingChunks = createMovingChunks(words, format.wordCount); 88 | 89 | let chunkIndex = 0; 90 | 91 | for (const movingChunk of movingChunks) { 92 | const wordOffset = ++chunkIndex * format.wordCount; 93 | 94 | const subject = movingChunk.join(' '); 95 | 96 | if (format.dateFnsFormat === 'R') { 97 | if (!configuration.locale) { 98 | log.trace('cannot attempt format without `locale` configuration'); 99 | } else if (!configuration.timezone) { 100 | log.trace('cannot attempt format without `timezone` configuration'); 101 | } else { 102 | const maybeDate = extractRelativeDate(subject, configuration.locale, configuration.timezone); 103 | 104 | if (maybeDate) { 105 | words = words.slice(wordOffset); 106 | 107 | log.debug('matched "%s" input using "%s" format (%s direction)', subject, format.dateFnsFormat, format.direction || 'no'); 108 | 109 | matches.push({ 110 | date: maybeDate, 111 | }); 112 | } 113 | } 114 | } else if (format.dateFnsFormat === 'EEE' || format.dateFnsFormat === 'EEEE') { 115 | const date = parseDate( 116 | subject, 117 | format.dateFnsFormat, 118 | baseDate, 119 | { 120 | locale: dateFnsLocale, 121 | }, 122 | ); 123 | 124 | if (isValidDate(date)) { 125 | words = words.slice(wordOffset); 126 | 127 | log.debug('matched "%s" input using "%s" format (%s direction)', subject, format.dateFnsFormat, format.direction || 'no'); 128 | 129 | matches.push({ 130 | date: formatDate(date, 'yyyy-MM-dd'), 131 | }); 132 | } 133 | } else { 134 | const yearIsExplicit = typeof format.yearIsExplicit === 'boolean' ? format.yearIsExplicit : true; 135 | 136 | if (yearIsExplicit) { 137 | const date = parseDate( 138 | subject, 139 | format.dateFnsFormat, 140 | baseDate, 141 | { 142 | locale: dateFnsLocale, 143 | }, 144 | ); 145 | 146 | if (!isValidDate(date)) { 147 | continue; 148 | } 149 | 150 | const formatDirection = format.direction; 151 | const configurationDirection = configuration.direction; 152 | 153 | if (formatDirection && configurationDirection && format.dateFnsFormat.includes('yyyy') && formatDirection.replace('Y', '') === configurationDirection.replace('Y', '')) { 154 | log.debug('matched format using yyyy; month-day direction matches'); 155 | } else if (format.direction && format.direction !== configuration.direction) { 156 | log.trace('discarding match; direction mismatch'); 157 | 158 | continue; 159 | } 160 | 161 | if (format.direction && !configuration.direction) { 162 | log.info('found a match using "%s" format; unsafe to use without `direction` configuration', format.dateFnsFormat); 163 | 164 | continue; 165 | } 166 | 167 | words = words.slice(wordOffset); 168 | 169 | log.debug('matched "%s" input using "%s" format (%s direction)', subject, format.dateFnsFormat, format.direction || 'no'); 170 | 171 | matches.push({ 172 | date: formatDate(date, 'yyyy-MM-dd'), 173 | }); 174 | } else { 175 | const date = parseDate( 176 | subject, 177 | format.dateFnsFormat, 178 | baseDate, 179 | { 180 | locale: dateFnsLocale, 181 | }, 182 | ); 183 | 184 | if (!isValidDate(date)) { 185 | continue; 186 | } 187 | 188 | const currentYear = parseInt(formatDate(baseDate, 'yyyy'), 10); 189 | 190 | const currentMonth = parseInt(formatDate(baseDate, 'M'), 10) + currentYear * 12; 191 | const parsedMonth = parseInt(formatDate(date, 'M'), 10) + parseInt(formatDate(date, 'yyyy'), 10) * 12; 192 | const difference = parsedMonth - currentMonth; 193 | 194 | let useYear; 195 | 196 | if (difference >= configuration.maximumAge) { 197 | useYear = currentYear - 1; 198 | } else if (difference < 0 && Math.abs(difference) >= configuration.minimumAge) { 199 | useYear = currentYear + 1; 200 | } else { 201 | useYear = currentYear; 202 | } 203 | 204 | const maybeDate = parseDate( 205 | useYear + '-' + formatDate(date, 'MM-dd'), 206 | 'yyyy-MM-dd', 207 | baseDate, 208 | { 209 | locale: dateFnsLocale, 210 | }, 211 | ); 212 | 213 | if (!isValidDate(maybeDate)) { 214 | continue; 215 | } 216 | 217 | if (format.direction && format.direction !== configuration.direction) { 218 | log.trace('discarding match; direction mismatch'); 219 | 220 | continue; 221 | } 222 | 223 | if (format.direction && !configuration.direction) { 224 | log.info('found a match using "%s" format; unsafe to use without "format" configuration', format.dateFnsFormat); 225 | 226 | continue; 227 | } 228 | 229 | words = words.slice(wordOffset); 230 | 231 | log.debug('matched "%s" input using "%s" format (%s direction)', subject, format.dateFnsFormat, format.direction || 'no'); 232 | 233 | matches.push({ 234 | date: formatDate(maybeDate, 'yyyy-MM-dd'), 235 | }); 236 | } 237 | } 238 | } 239 | } 240 | 241 | return matches; 242 | }; 243 | -------------------------------------------------------------------------------- /src/extractRelativeDate.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import moment from 'moment-timezone'; 4 | import dictionary from 'relative-date-names'; 5 | 6 | export default (subject: string, locale: string, timezone: string): ?string => { 7 | const translation = dictionary[locale]; 8 | 9 | if (!translation) { 10 | throw new Error('No translation available for the target locale.'); 11 | } 12 | 13 | if (!moment.tz.zone(timezone)) { 14 | throw new Error('Unrecognized timezone.'); 15 | } 16 | 17 | const normalizedSubject = subject.toLowerCase(); 18 | 19 | const now = moment(); 20 | 21 | if (normalizedSubject === translation.day.relative.yesterday) { 22 | return now.subtract(1, 'day').format('YYYY-MM-DD'); 23 | } 24 | 25 | if (normalizedSubject === translation.day.relative.today) { 26 | return now.format('YYYY-MM-DD'); 27 | } 28 | 29 | if (normalizedSubject === translation.day.relative.tomorrow) { 30 | return now.add(1, 'day').format('YYYY-MM-DD'); 31 | } 32 | 33 | return null; 34 | }; 35 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | /* eslint-disable filenames/match-exported */ 4 | import extractDate from './extractDate'; 5 | 6 | export default extractDate; 7 | -------------------------------------------------------------------------------- /src/normalizeInput.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default (input: string): string => { 4 | let lastInput = input; 5 | 6 | while (true) { 7 | const result = lastInput 8 | 9 | // 2019-02-12T00:00:00 10 | .replace(/(\d+)T(\d+)/, '$1 $2') 11 | 12 | .replace(/(\d+)\s\/\s(\d+)/, '$1/$2') 13 | 14 | // Ignore commas to capture formats such as 'Monday 20, May 15 | .replace(/,/g, ' ') 16 | 17 | .replace(/[.:;] /g, ' ') 18 | .replace(/\s+/g, ' ') 19 | .trim(); 20 | 21 | if (result === lastInput) { 22 | return result; 23 | } 24 | 25 | lastInput = result; 26 | } 27 | 28 | throw new Error('Unexpected state.'); 29 | }; 30 | -------------------------------------------------------------------------------- /src/types.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export type DateMatchType = {| 4 | +date: string, 5 | |}; 6 | 7 | export type DirectionType = 'DM' | 'DMY' | 'DYM' | 'MD' | 'YDM' | 'YMD' | 'MDY'; 8 | 9 | export type UserConfigurationType = {| 10 | +direction?: DirectionType, 11 | +locale?: string, 12 | +maximumAge?: number, 13 | +minimumAge?: number, 14 | +timezone?: string, 15 | |}; 16 | 17 | export type ConfigurationType = {| 18 | +direction?: DirectionType, 19 | +locale?: string, 20 | +maximumAge: number, 21 | +minimumAge: number, 22 | +timezone?: string, 23 | |}; 24 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "canonical/ava" 4 | ], 5 | "rules": { 6 | "filenames/match-regex": 0, 7 | "id-length": 0, 8 | "no-restricted-syntax": 0 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/extract-date/calculateSpecificity.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test from 'ava'; 4 | import cartesian from 'cartesian'; 5 | import calculateSpecificity from '../../src/calculateSpecificity'; 6 | 7 | test('tokens including yyyy, (MMMM, MM, MM, M), (dd, d, do) have the highest specificity', (t) => { 8 | const formats = cartesian([ 9 | [ 10 | 'yyyy', 11 | ], 12 | [ 13 | 'MMMM', 14 | 'MMM', 15 | 'MM', 16 | 'M', 17 | ], 18 | [ 19 | 'dd', 20 | 'd', 21 | 'do', 22 | ], 23 | ]); 24 | 25 | for (const tokens of formats) { 26 | const format = tokens.join(' '); 27 | 28 | t.is(calculateSpecificity(format), 80 + format.length); 29 | } 30 | }); 31 | 32 | test('tokens without year, month or month date have the lowest specificity', (t) => { 33 | const formats = [ 34 | 'EEEE', 35 | 'EEE', 36 | 'R', 37 | ]; 38 | 39 | for (const format of formats) { 40 | t.is(calculateSpecificity(format), format.length); 41 | } 42 | }); 43 | 44 | test('adds format length to the specificity', (t) => { 45 | t.is(calculateSpecificity('xxxx'), 4); 46 | t.is(calculateSpecificity('xx'), 2); 47 | }); 48 | -------------------------------------------------------------------------------- /test/extract-date/createFormats.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test from 'ava'; 4 | import createFormats from '../../src/createFormats'; 5 | 6 | const expectedOrder = `MMMM yyyy EEE do 7 | MMMM yyyy EEE d 8 | do MMMM yyyy 9 | MMMM do yyyy 10 | MMMM yyyy do 11 | d MMMM yyyy 12 | do MMM yyyy 13 | MMM yyyy do 14 | MMMM d yyyy 15 | MMMM yyyy d 16 | d MMM yyyy 17 | MMM yyyy d 18 | EEEE dd MMMM 19 | EEEE do MMMM 20 | EEEE MMMM dd 21 | EEEE MMMM do 22 | EEE dd MMMM 23 | EEE do MMMM 24 | EEE MMMM dd 25 | EEE MMMM do 26 | EEEE d MMMM 27 | EEEE dd MMM 28 | EEEE do MMM 29 | EEEE MMM dd 30 | EEEE MMM do 31 | EEEE MMMM d 32 | EEE d MMMM 33 | EEE dd MMM 34 | EEE do MMM 35 | EEE MMM dd 36 | EEE MMM do 37 | EEE MMMM d 38 | EEEE d MMM 39 | EEEE MMM d 40 | EEE d MMM 41 | EEE MMM d 42 | dd MMMM 43 | do MMMM 44 | MMMM dd 45 | MMMM do 46 | d MMMM 47 | dd MMM 48 | do MMM 49 | MMM dd 50 | MMM do 51 | MMMM d 52 | d MMM 53 | MMM d 54 | dd-MM-yyyy 55 | dd.MM.yyyy 56 | dd/MM/yyyy 57 | MM-dd-yyyy 58 | MM.dd.yyyy 59 | MM/dd/yyyy 60 | yyyy-MM-dd 61 | yyyy.dd.MM 62 | yyyy.MM.dd 63 | yyyy/MM/dd 64 | d-M-yyyy 65 | d.M.yyyy 66 | d/M/yyyy 67 | M-d-yyyy 68 | M.d.yyyy 69 | M/d/yyyy 70 | yyyy-M-d 71 | yyyy.d.M 72 | yyyy.M.d 73 | yyyy/M/d 74 | yyyyMMdd 75 | dd.MM.yy 76 | dd/MM/yy 77 | MM/dd/yy 78 | d.M.yy 79 | d/M/yy 80 | M/d/yy 81 | dd-MM 82 | dd.MM 83 | dd/MM 84 | MM-dd 85 | MM.dd 86 | MM/dd 87 | d-MM 88 | d.MM 89 | d/MM 90 | dd-M 91 | dd.M 92 | dd/M 93 | M-dd 94 | M.dd 95 | M/dd 96 | MM-d 97 | MM.d 98 | MM/d 99 | d-M 100 | d.M 101 | d/M 102 | M-d 103 | M.d 104 | M/d 105 | EEEE 106 | EEE 107 | R`; 108 | 109 | test('orders formats by their specificity (resolves conflicts using localeCompare)', (t) => { 110 | const formats = createFormats(); 111 | 112 | const order = formats 113 | .map((format) => { 114 | return format.dateFnsFormat; 115 | }) 116 | .join('\n'); 117 | 118 | // eslint-disable-next-line ava/prefer-power-assert 119 | t.is(order, expectedOrder); 120 | }); 121 | -------------------------------------------------------------------------------- /test/extract-date/createMovingChunks.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test from 'ava'; 4 | import createMovingChunks from '../../src/createMovingChunks'; 5 | 6 | test('creates an array of fixed length text slices, each offset by 1 character', (t) => { 7 | t.deepEqual(createMovingChunks(['a', 'b', 'c', 'd'], 2), [ 8 | [ 9 | 'a', 10 | 'b', 11 | ], 12 | [ 13 | 'b', 14 | 'c', 15 | ], 16 | [ 17 | 'c', 18 | 'd', 19 | ], 20 | ]); 21 | 22 | t.deepEqual(createMovingChunks(['a', 'b', 'c'], 2), [ 23 | [ 24 | 'a', 25 | 'b', 26 | ], 27 | [ 28 | 'b', 29 | 'c', 30 | ], 31 | ]); 32 | }); 33 | -------------------------------------------------------------------------------- /test/extract-date/extractDate/configuration.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test, { 4 | afterEach, 5 | beforeEach, 6 | } from 'ava'; 7 | import sinon from 'sinon'; 8 | import extractDate from '../../../src/extractDate'; 9 | 10 | let clock; 11 | 12 | beforeEach(() => { 13 | clock = sinon.useFakeTimers(); 14 | }); 15 | 16 | afterEach(() => { 17 | clock.restore(); 18 | }); 19 | 20 | test('throws an error if invalid `locale` is provided', (t) => { 21 | const error = t.throws(() => { 22 | extractDate('foo', { 23 | locale: 'bar', 24 | }); 25 | }); 26 | 27 | t.is(error.message, 'No translation available for the target locale (date-fns).'); 28 | }); 29 | 30 | test('throws an error if invalid `timezone` is provided', (t) => { 31 | const error = t.throws(() => { 32 | extractDate('foo', { 33 | timezone: 'bar', 34 | }); 35 | }); 36 | 37 | t.is(error.message, 'Unrecognized timezone.'); 38 | }); 39 | 40 | test('throws an error if invalid `maximumAge` is a negative value', (t) => { 41 | const error = t.throws(() => { 42 | extractDate('foo', { 43 | maximumAge: -1, 44 | }); 45 | }); 46 | 47 | t.is(error.message, '`maximumAge` must be a positive number.'); 48 | }); 49 | 50 | test('throws an error if invalid `minimumAge` is a negative value', (t) => { 51 | const error = t.throws(() => { 52 | extractDate('foo', { 53 | minimumAge: -1, 54 | }); 55 | }); 56 | 57 | t.is(error.message, '`minimumAge` must be a positive number.'); 58 | }); 59 | -------------------------------------------------------------------------------- /test/extract-date/extractDate/edge-cases.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test, { 4 | afterEach, 5 | beforeEach, 6 | } from 'ava'; 7 | import sinon from 'sinon'; 8 | import { 9 | parse as parseDate, 10 | } from 'date-fns'; 11 | import extractDate from '../../../src/extractDate'; 12 | 13 | beforeEach((t) => { 14 | t.context.clock = sinon.useFakeTimers(); 15 | }); 16 | 17 | afterEach((t) => { 18 | t.context.clock.restore(); 19 | }); 20 | 21 | test('parses ISO 8601 date-time format', (t) => { 22 | t.context.clock.tick(parseDate('2000-01-02', 'yyyy-MM-dd', new Date()).getTime()); 23 | 24 | t.deepEqual(extractDate('2000-01-02T00:00'), [{date: '2000-01-02'}]); 25 | }); 26 | 27 | test('extracts date matching multiple formats once', (t) => { 28 | t.context.clock.tick(parseDate('2000-01-02', 'yyyy-MM-dd', new Date()).getTime()); 29 | 30 | // 'foo bar' prefix is required to ensure that substring matching is correctly advancing. 31 | t.deepEqual(extractDate('foo bar 02/01/2000', {direction: 'DMY'}), [{date: '2000-01-02'}]); 32 | }); 33 | 34 | test('full-year formats are used regardless of whether direction defines Y', (t) => { 35 | t.context.clock.tick(parseDate('2000-01-02', 'yyyy-MM-dd', new Date()).getTime()); 36 | 37 | t.deepEqual(extractDate('02/01/2020', {direction: 'DM'}), [{date: '2020-01-02'}]); 38 | }); 39 | 40 | test.todo('interprets 24:00 time as the next date'); 41 | -------------------------------------------------------------------------------- /test/extract-date/extractDate/fixtures.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test, { 4 | afterEach, 5 | beforeEach, 6 | } from 'ava'; 7 | import sinon from 'sinon'; 8 | import { 9 | parse as parseDate, 10 | } from 'date-fns'; 11 | import fixtureDates from '../../fixtures/dates.json'; 12 | import extractDate from '../../../src/extractDate'; 13 | 14 | beforeEach((t) => { 15 | t.context.clock = sinon.useFakeTimers(); 16 | }); 17 | 18 | afterEach((t) => { 19 | t.context.clock.restore(); 20 | }); 21 | 22 | const normalizedFixtureDates = fixtureDates 23 | .map((fixture) => { 24 | return { 25 | ...fixture, 26 | subject: fixture.subject.trim(), 27 | }; 28 | }) 29 | .map((fixture) => { 30 | return JSON.stringify(fixture); 31 | }) 32 | .filter((fixture, index, self) => { 33 | return self.indexOf(fixture) === index; 34 | }) 35 | .map((fixture) => { 36 | return JSON.parse(fixture); 37 | }) 38 | .map((fixture) => { 39 | return { 40 | ...fixture, 41 | configuration: { 42 | ...fixture.configuration, 43 | maximumAge: Infinity, 44 | minimumAge: Infinity, 45 | }, 46 | }; 47 | }); 48 | 49 | for (const fixtureDate of normalizedFixtureDates) { 50 | test('extracts dates from "' + fixtureDate.subject + '" fixture using ' + JSON.stringify(fixtureDate.configuration) + ' configuration on ' + fixtureDate.date + ' date', (t) => { 51 | t.context.clock.tick(parseDate(fixtureDate.date, 'yyyy-MM-dd', new Date()).getTime()); 52 | 53 | t.deepEqual(extractDate(fixtureDate.subject, fixtureDate.configuration), fixtureDate.matches); 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /test/extract-date/extractDate/general-formats.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test, { 4 | afterEach, 5 | beforeEach, 6 | } from 'ava'; 7 | import sinon from 'sinon'; 8 | import { 9 | format as formatDate, 10 | parse as parseDate, 11 | } from 'date-fns'; 12 | import extractDate from '../../../src/extractDate'; 13 | import createFormats from '../../../src/createFormats'; 14 | 15 | let clock; 16 | 17 | beforeEach(() => { 18 | clock = sinon.useFakeTimers(); 19 | }); 20 | 21 | afterEach(() => { 22 | clock.restore(); 23 | }); 24 | 25 | // https://en.wikipedia.org/wiki/Date_format_by_country 26 | // %w arbitrary white-space separated text 27 | 28 | const describeConfiguration = (userConfiguration) => { 29 | const configuration = {}; 30 | 31 | if (userConfiguration.direction) { 32 | configuration.direction = userConfiguration.direction; 33 | } 34 | 35 | return JSON.stringify(configuration); 36 | }; 37 | 38 | const formats = createFormats(); 39 | 40 | const subjects = formats 41 | .filter((format) => { 42 | return format.test !== false; 43 | }) 44 | .map((format) => { 45 | clock = sinon.useFakeTimers(); 46 | clock.tick(parseDate('2000-06-01', 'yyyy-MM-dd', new Date()).getTime()); 47 | 48 | const currentDate = new Date(); 49 | 50 | return { 51 | date: formatDate(currentDate, 'yyyy-MM-dd'), 52 | dateFnsFormat: format.dateFnsFormat, 53 | direction: format.direction, 54 | input: formatDate(currentDate, format.dateFnsFormat), 55 | }; 56 | }); 57 | 58 | for (const subject of subjects) { 59 | // eslint-disable-next-line no-loop-func 60 | test('extracts ' + subject.dateFnsFormat + ' from "' + subject.input + '" input using ' + describeConfiguration(subject) + ' configuration', (t) => { 61 | clock.tick(parseDate('2000-06-01', 'yyyy-MM-dd', new Date()).getTime()); 62 | 63 | const actual = extractDate(subject.input, subject); 64 | const expected = subject.date; 65 | 66 | t.is(actual.length, 1); 67 | t.is(actual[0].date, expected); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /test/extract-date/extractDate/implied-year.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test, { 4 | afterEach, 5 | beforeEach, 6 | } from 'ava'; 7 | import sinon from 'sinon'; 8 | import moment from 'moment'; 9 | import extractDate from '../../../src/extractDate'; 10 | 11 | let clock; 12 | 13 | beforeEach(() => { 14 | clock = sinon.useFakeTimers(); 15 | }); 16 | 17 | afterEach(() => { 18 | clock.restore(); 19 | }); 20 | 21 | test('assumes last year if month difference is greater or equal to `maximumAge`', (t) => { 22 | clock.tick(moment('2000-01-01').valueOf()); 23 | 24 | const configuration = { 25 | direction: 'MD', 26 | maximumAge: 10, 27 | }; 28 | 29 | t.deepEqual(extractDate('01-01', configuration), [{date: '2000-01-01'}]); 30 | t.deepEqual(extractDate('02-01', configuration), [{date: '2000-02-01'}]); 31 | t.deepEqual(extractDate('03-01', configuration), [{date: '2000-03-01'}]); 32 | t.deepEqual(extractDate('04-01', configuration), [{date: '2000-04-01'}]); 33 | t.deepEqual(extractDate('05-01', configuration), [{date: '2000-05-01'}]); 34 | t.deepEqual(extractDate('06-01', configuration), [{date: '2000-06-01'}]); 35 | t.deepEqual(extractDate('07-01', configuration), [{date: '2000-07-01'}]); 36 | t.deepEqual(extractDate('08-01', configuration), [{date: '2000-08-01'}]); 37 | t.deepEqual(extractDate('09-01', configuration), [{date: '2000-09-01'}]); 38 | t.deepEqual(extractDate('10-01', configuration), [{date: '2000-10-01'}]); 39 | 40 | t.deepEqual(extractDate('11-01', configuration), [{date: '1999-11-01'}]); 41 | t.deepEqual(extractDate('12-01', configuration), [{date: '1999-12-01'}]); 42 | }); 43 | 44 | test('does not assume last year when `maximumAge` is `Infinity`', (t) => { 45 | clock.tick(moment('2000-01-01').valueOf()); 46 | 47 | const configuration = { 48 | direction: 'MD', 49 | maximumAge: Infinity, 50 | }; 51 | 52 | t.deepEqual(extractDate('01-01', configuration), [{date: '2000-01-01'}]); 53 | t.deepEqual(extractDate('02-01', configuration), [{date: '2000-02-01'}]); 54 | t.deepEqual(extractDate('03-01', configuration), [{date: '2000-03-01'}]); 55 | t.deepEqual(extractDate('04-01', configuration), [{date: '2000-04-01'}]); 56 | t.deepEqual(extractDate('05-01', configuration), [{date: '2000-05-01'}]); 57 | t.deepEqual(extractDate('06-01', configuration), [{date: '2000-06-01'}]); 58 | t.deepEqual(extractDate('07-01', configuration), [{date: '2000-07-01'}]); 59 | t.deepEqual(extractDate('08-01', configuration), [{date: '2000-08-01'}]); 60 | t.deepEqual(extractDate('09-01', configuration), [{date: '2000-09-01'}]); 61 | t.deepEqual(extractDate('10-01', configuration), [{date: '2000-10-01'}]); 62 | t.deepEqual(extractDate('11-01', configuration), [{date: '2000-11-01'}]); 63 | t.deepEqual(extractDate('12-01', configuration), [{date: '2000-12-01'}]); 64 | }); 65 | 66 | test('increments year value if month difference is greater or equal to `minimumAge`', (t) => { 67 | clock.tick(moment('2000-12-01').valueOf()); 68 | 69 | const configuration = { 70 | direction: 'MD', 71 | minimumAge: 2, 72 | }; 73 | 74 | t.deepEqual(extractDate('01-01', configuration), [{date: '2001-01-01'}]); 75 | t.deepEqual(extractDate('02-01', configuration), [{date: '2001-02-01'}]); 76 | t.deepEqual(extractDate('03-01', configuration), [{date: '2001-03-01'}]); 77 | t.deepEqual(extractDate('04-01', configuration), [{date: '2001-04-01'}]); 78 | t.deepEqual(extractDate('05-01', configuration), [{date: '2001-05-01'}]); 79 | t.deepEqual(extractDate('06-01', configuration), [{date: '2001-06-01'}]); 80 | t.deepEqual(extractDate('07-01', configuration), [{date: '2001-07-01'}]); 81 | t.deepEqual(extractDate('08-01', configuration), [{date: '2001-08-01'}]); 82 | t.deepEqual(extractDate('09-01', configuration), [{date: '2001-09-01'}]); 83 | t.deepEqual(extractDate('10-01', configuration), [{date: '2001-10-01'}]); 84 | 85 | t.deepEqual(extractDate('11-01', configuration), [{date: '2000-11-01'}]); 86 | t.deepEqual(extractDate('12-01', configuration), [{date: '2000-12-01'}]); 87 | }); 88 | 89 | test('does not increment year value if `minimumAge` is `Infinity`', (t) => { 90 | clock.tick(moment('2000-12-01').valueOf()); 91 | 92 | const configuration = { 93 | direction: 'MD', 94 | minimumAge: Infinity, 95 | }; 96 | 97 | t.deepEqual(extractDate('01-01', configuration), [{date: '2000-01-01'}]); 98 | t.deepEqual(extractDate('02-01', configuration), [{date: '2000-02-01'}]); 99 | t.deepEqual(extractDate('03-01', configuration), [{date: '2000-03-01'}]); 100 | t.deepEqual(extractDate('04-01', configuration), [{date: '2000-04-01'}]); 101 | t.deepEqual(extractDate('05-01', configuration), [{date: '2000-05-01'}]); 102 | t.deepEqual(extractDate('06-01', configuration), [{date: '2000-06-01'}]); 103 | t.deepEqual(extractDate('07-01', configuration), [{date: '2000-07-01'}]); 104 | t.deepEqual(extractDate('08-01', configuration), [{date: '2000-08-01'}]); 105 | t.deepEqual(extractDate('09-01', configuration), [{date: '2000-09-01'}]); 106 | t.deepEqual(extractDate('10-01', configuration), [{date: '2000-10-01'}]); 107 | t.deepEqual(extractDate('11-01', configuration), [{date: '2000-11-01'}]); 108 | t.deepEqual(extractDate('12-01', configuration), [{date: '2000-12-01'}]); 109 | }); 110 | 111 | test('`maximumAge` and `minimumAge` can be combined', (t) => { 112 | clock.tick(moment('2000-06-01').valueOf()); 113 | 114 | const configuration = { 115 | direction: 'MD', 116 | maximumAge: 2, 117 | minimumAge: 2, 118 | }; 119 | 120 | t.deepEqual(extractDate('01-01', configuration), [{date: '2001-01-01'}]); 121 | t.deepEqual(extractDate('02-01', configuration), [{date: '2001-02-01'}]); 122 | t.deepEqual(extractDate('03-01', configuration), [{date: '2001-03-01'}]); 123 | t.deepEqual(extractDate('04-01', configuration), [{date: '2001-04-01'}]); 124 | 125 | t.deepEqual(extractDate('05-01', configuration), [{date: '2000-05-01'}]); 126 | t.deepEqual(extractDate('06-01', configuration), [{date: '2000-06-01'}]); 127 | t.deepEqual(extractDate('07-01', configuration), [{date: '2000-07-01'}]); 128 | 129 | t.deepEqual(extractDate('08-01', configuration), [{date: '1999-08-01'}]); 130 | t.deepEqual(extractDate('09-01', configuration), [{date: '1999-09-01'}]); 131 | t.deepEqual(extractDate('10-01', configuration), [{date: '1999-10-01'}]); 132 | t.deepEqual(extractDate('11-01', configuration), [{date: '1999-11-01'}]); 133 | t.deepEqual(extractDate('12-01', configuration), [{date: '1999-12-01'}]); 134 | }); 135 | -------------------------------------------------------------------------------- /test/extract-date/extractDate/localised.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test, { 4 | afterEach, 5 | beforeEach, 6 | } from 'ava'; 7 | import sinon from 'sinon'; 8 | import { 9 | parse as parseDate, 10 | } from 'date-fns'; 11 | import extractDate from '../../../src/extractDate'; 12 | 13 | let clock; 14 | 15 | beforeEach(() => { 16 | clock = sinon.useFakeTimers(); 17 | }); 18 | 19 | afterEach(() => { 20 | clock.restore(); 21 | }); 22 | 23 | test('extracts a localised date (English)', (t) => { 24 | clock.tick(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime()); 25 | 26 | const configuration = { 27 | locale: 'en', 28 | }; 29 | 30 | t.deepEqual(extractDate('May 1, 2017', configuration), [{date: '2017-05-01'}]); 31 | }); 32 | 33 | test('extracts a localised date (French)', (t) => { 34 | clock.tick(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime()); 35 | 36 | const configuration = { 37 | locale: 'fr', 38 | }; 39 | 40 | t.deepEqual(extractDate('Mai 1, 2017', configuration), [{date: '2017-05-01'}]); 41 | }); 42 | -------------------------------------------------------------------------------- /test/extract-date/extractDate/multiple-dates.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test, { 4 | afterEach, 5 | beforeEach, 6 | } from 'ava'; 7 | import sinon from 'sinon'; 8 | import { 9 | addDays, 10 | format as formatDate, 11 | parse as parseDate, 12 | } from 'date-fns'; 13 | import extractDate from '../../../src/extractDate'; 14 | 15 | let clock; 16 | 17 | beforeEach(() => { 18 | clock = sinon.useFakeTimers(); 19 | }); 20 | 21 | afterEach(() => { 22 | clock.restore(); 23 | }); 24 | 25 | test('extracts multiple dates', (t) => { 26 | clock.tick(parseDate('2000-06-01', 'yyyy-MM-dd', new Date()).valueOf()); 27 | 28 | const actual = extractDate(formatDate(new Date(), 'yyyy-MM-dd') + ' ' + formatDate(addDays(new Date(), 1), 'yyyy-MM-dd')); 29 | const expected = [ 30 | { 31 | date: formatDate(new Date(), 'yyyy-MM-dd'), 32 | }, 33 | { 34 | date: formatDate(addDays(new Date(), 1), 'yyyy-MM-dd'), 35 | }, 36 | ]; 37 | 38 | t.deepEqual(actual, expected); 39 | }); 40 | -------------------------------------------------------------------------------- /test/extract-date/extractDate/relative-dates.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test, { 4 | afterEach, 5 | beforeEach, 6 | } from 'ava'; 7 | import sinon from 'sinon'; 8 | import { 9 | parse as parseDate, 10 | } from 'date-fns'; 11 | import extractDate from '../../../src/extractDate'; 12 | 13 | let clock; 14 | 15 | beforeEach(() => { 16 | clock = sinon.useFakeTimers(); 17 | }); 18 | 19 | afterEach(() => { 20 | clock.restore(); 21 | }); 22 | 23 | test('does not extract relative dates when locale is undefined', (t) => { 24 | clock.tick(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime()); 25 | 26 | const configuration = { 27 | timezone: 'Europe/London', 28 | }; 29 | 30 | t.deepEqual(extractDate('today', configuration), []); 31 | }); 32 | 33 | test('does not extract relative dates when timezone is undefined', (t) => { 34 | clock.tick(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime()); 35 | 36 | const configuration = { 37 | locale: 'en', 38 | }; 39 | 40 | t.deepEqual(extractDate('today', configuration), []); 41 | }); 42 | 43 | test('extracts relative date (yesterday)', (t) => { 44 | clock.tick(parseDate('2000-01-02', 'yyyy-MM-dd', new Date()).getTime()); 45 | 46 | const configuration = { 47 | locale: 'en', 48 | timezone: 'Europe/London', 49 | }; 50 | 51 | t.deepEqual(extractDate('yesterday', configuration), [{date: '2000-01-01'}]); 52 | }); 53 | 54 | test('extracts relative date (today)', (t) => { 55 | clock.tick(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime()); 56 | 57 | const configuration = { 58 | locale: 'en', 59 | timezone: 'Europe/London', 60 | }; 61 | 62 | t.deepEqual(extractDate('today', configuration), [{date: '2000-01-01'}]); 63 | }); 64 | 65 | test('extracts relative date (tomorrow)', (t) => { 66 | clock.tick(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime()); 67 | 68 | const configuration = { 69 | locale: 'en', 70 | timezone: 'Europe/London', 71 | }; 72 | 73 | t.deepEqual(extractDate('tomorrow', configuration), [{date: '2000-01-02'}]); 74 | }); 75 | -------------------------------------------------------------------------------- /test/extract-date/extractRelativeDate.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test, { 4 | afterEach, 5 | beforeEach, 6 | } from 'ava'; 7 | import sinon from 'sinon'; 8 | import moment from 'moment'; 9 | import extractRelativeDate from '../../src/extractRelativeDate'; 10 | 11 | let clock; 12 | 13 | beforeEach(() => { 14 | clock = sinon.useFakeTimers(); 15 | }); 16 | 17 | afterEach(() => { 18 | clock.restore(); 19 | }); 20 | 21 | test('extracts relative date (yesterday)', (t) => { 22 | clock.tick(moment('2018-07-08').valueOf()); 23 | 24 | t.is(extractRelativeDate('yesterday', 'en', 'Europe/London'), '2018-07-07'); 25 | }); 26 | 27 | test('extracts relative date (today)', (t) => { 28 | clock.tick(moment('2018-07-08').valueOf()); 29 | 30 | t.is(extractRelativeDate('today', 'en', 'Europe/London'), '2018-07-08'); 31 | }); 32 | 33 | test('extracts relative date (tomorrow)', (t) => { 34 | clock.tick(moment('2018-07-08').valueOf()); 35 | 36 | t.is(extractRelativeDate('tomorrow', 'en', 'Europe/London'), '2018-07-09'); 37 | }); 38 | 39 | test('supports different locales', (t) => { 40 | clock.tick(moment('2018-07-08').valueOf()); 41 | 42 | t.is(extractRelativeDate('rytoj', 'lt', 'Europe/London'), '2018-07-09'); 43 | }); 44 | 45 | test('returns null when date cannot be recognized', (t) => { 46 | clock.tick(moment('2018-07-08').valueOf()); 47 | 48 | t.is(extractRelativeDate('foo', 'en', 'Europe/London'), null); 49 | }); 50 | -------------------------------------------------------------------------------- /test/extract-date/normalizeInput.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test from 'ava'; 4 | import normalizeInput from '../../src/normalizeInput'; 5 | 6 | test('splits yyyy-MM-ddTHH:mm', (t) => { 7 | t.is(normalizeInput('2018-01-01T14:00 2018-01-02T15:00'), '2018-01-01 14:00 2018-01-02 15:00'); 8 | }); 9 | 10 | test('removes spaces between date-like fragments separated by /', (t) => { 11 | t.is(normalizeInput('10 / 20 / 30'), '10/20/30'); 12 | }); 13 | 14 | test('removes repeating white space', (t) => { 15 | t.is(normalizeInput('foo bar baz'), 'foo bar baz'); 16 | }); 17 | 18 | test('trims white space', (t) => { 19 | t.is(normalizeInput(' foo '), 'foo'); 20 | }); 21 | -------------------------------------------------------------------------------- /test/fixtures/README.md: -------------------------------------------------------------------------------- 1 | These fixtures are extracted from a project that earlier used strict patterns to extract dates from a string. 2 | --------------------------------------------------------------------------------