├── .npmignore
├── flow-typed.config.json
├── examples
└── simple
│ ├── index.html
│ └── index.js
├── .babelrc.js
├── CONTRIBUTORS.ms
├── .flowconfig
├── src
├── types.js
├── language-strings
│ ├── tr.js
│ ├── fr-short.js
│ ├── az-short.js
│ ├── en-short.js
│ ├── es-short.js
│ ├── it-short.js
│ ├── pt-short.js
│ ├── tr-short.js
│ ├── de-short.js
│ ├── it.js
│ ├── ka-short.js
│ ├── pt-br-short.js
│ ├── az.js
│ ├── en-fuzzy-short.js
│ ├── hy.js
│ ├── ja.js
│ ├── ro.js
│ ├── zh-CN.js
│ ├── bg.js
│ ├── mk.js
│ ├── gl.js
│ ├── zh-TW.js
│ ├── es.js
│ ├── lt.js
│ ├── lv.js
│ ├── pt-br.js
│ ├── sk.js
│ ├── ko.js
│ ├── pt.js
│ ├── uz.js
│ ├── da.js
│ ├── eu.js
│ ├── is.js
│ ├── so.js
│ ├── no.js
│ ├── af.js
│ ├── ca.js
│ ├── cy.js
│ ├── rw.js
│ ├── oc.js
│ ├── de.js
│ ├── el.js
│ ├── hi.js
│ ├── si.js
│ ├── sv.js
│ ├── fr.js
│ ├── id.js
│ ├── vi.js
│ ├── en-fuzzy.js
│ ├── th.js
│ ├── en.js
│ ├── hu.js
│ ├── nl.js
│ ├── jv.js
│ ├── ka.js
│ ├── ta.js
│ ├── dv.js
│ ├── he.js
│ ├── pl.js
│ ├── fi.js
│ ├── et.js
│ ├── ky.js
│ ├── fa-short.js
│ ├── be.js
│ ├── uk.js
│ ├── rs.js
│ ├── sr.js
│ ├── bs.js
│ ├── hr.js
│ ├── sl.js
│ ├── fa.js
│ ├── ru.js
│ ├── cs.js
│ └── ar.js
├── dateParser.js
├── defaultFormatter.js
├── formatters
│ └── buildFormatter.js
└── index.js
├── flow-typed
├── npm
│ ├── string-natural-compare_v3.x.x.js
│ ├── flow-enums-runtime_v0.x.x.js
│ ├── yargs_v17.x.x.js
│ └── jest_v29.x.x.js
├── intl.js
└── cssom.js
├── rollup.config.mjs
├── .gitignore
├── .prettierignore
├── __tests__
├── formatter-value-tests.js
└── index.js
├── .eslintrc
├── LICENSE
├── .github
└── workflows
│ └── build-and-deploy.yml
├── CHANGELOG.md
├── package.json
├── scripts
└── gen-types.js
└── README.md
/.npmignore:
--------------------------------------------------------------------------------
1 | __tests__
2 | coverage
3 | examples
4 | node_modules
5 | src
6 | .babelrc
7 | .eslintrc
8 | .flowignore
9 | .prettierignore
--------------------------------------------------------------------------------
/flow-typed.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": ["node", "dom", "bom", "intl", "cssom", "indexeddb", "serviceworkers", "webassembly", "jsx"]
3 | }
--------------------------------------------------------------------------------
/examples/simple/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Just a test
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.babelrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | process.env['ES6'] ? null : '@babel/env',
4 | '@babel/react',
5 | '@babel/flow',
6 | ].filter(Boolean),
7 | plugins: [['babel-plugin-syntax-hermes-parser', { flow: 'detect' }]],
8 | }
9 |
--------------------------------------------------------------------------------
/CONTRIBUTORS.ms:
--------------------------------------------------------------------------------
1 | # Thanks
2 |
3 | [Luke Karrys](https://github.com/lukekarrys)
4 | [Ian Johnson](https://github.com/ninjaferret)
5 | [Benjamin Flesch](https://github.com/bf)
6 | [Jonny Buchanan](https://github.com/insin)
7 | [Andrew Gremmo](https://github.com/andrewjgremmo)
8 | [Denis Sokolov](https://github.com/denis-sokolov)
9 | [Paul Bach](https://github.com/psolbach)
10 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | .*/lib/.*
3 | .*/es6/.*
4 | .*/coverage/.*
5 | .*/node_modules/module-deps/.*
6 | .*/malformed_package_json/.*
7 |
8 | [include]
9 |
10 | [libs]
11 |
12 | [options]
13 | emoji=true
14 | exact_by_default=true
15 | experimental.const_params=true
16 | module.use_strict=true
17 | munge_underscores=true
18 | suppress_type=$FlowFixMe
19 | suppress_type=$FlowTODO
20 |
--------------------------------------------------------------------------------
/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow strict
2 |
3 | export type Unit =
4 | | 'second'
5 | | 'minute'
6 | | 'hour'
7 | | 'day'
8 | | 'week'
9 | | 'month'
10 | | 'year'
11 |
12 | export type Suffix = 'ago' | 'from now'
13 |
14 | export type Formatter = (
15 | value: number,
16 | unit: Unit,
17 | suffix: Suffix,
18 | epochMilliseconds: number,
19 | nextFormatter: Formatter,
20 | now: () => number,
21 | ) => React.Node
--------------------------------------------------------------------------------
/flow-typed/npm/string-natural-compare_v3.x.x.js:
--------------------------------------------------------------------------------
1 | // flow-typed signature: 232f932594725cf979b6f7524097aceb
2 | // flow-typed version: d8a68b7d56/string-natural-compare_v3.x.x/flow_>=v0.83.x
3 |
4 | declare module 'string-natural-compare' {
5 | declare function naturalCompare(
6 | a: string,
7 | b: string,
8 | options?: {|
9 | caseInsensitive?: boolean,
10 | alphabet?: string,
11 | |},
12 | ): number;
13 |
14 | declare module.exports: typeof naturalCompare;
15 | }
16 |
--------------------------------------------------------------------------------
/src/language-strings/tr.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Turkish
5 | const strings: L10nsStrings = {
6 | suffixAgo: 'önce',
7 | suffixFromNow: 'sonra',
8 | second: '1 saniye',
9 | seconds: '%d saniye',
10 | minute: '1 dakika',
11 | minutes: '%d dakika',
12 | hour: '1 saat',
13 | hours: '%d saat',
14 | day: '1 gün',
15 | days: '%d gün',
16 | month: '1 ay',
17 | months: '%d ay',
18 | year: '1 yıl',
19 | years: '%d yıl',
20 | }
21 |
22 | export default strings
23 |
--------------------------------------------------------------------------------
/src/language-strings/fr-short.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // French shortened
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'il y a',
7 | prefixFromNow: "d'ici",
8 | seconds: "moins d'une minute",
9 | minute: 'une minute',
10 | minutes: '%d minutes',
11 | hour: 'une heure',
12 | hours: '%d heures',
13 | day: 'un jour',
14 | days: '%d jours',
15 | month: 'un mois',
16 | months: '%d mois',
17 | year: 'un an',
18 | years: '%d ans',
19 | }
20 |
21 | export default strings
22 |
--------------------------------------------------------------------------------
/src/language-strings/az-short.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | const strings: L10nsStrings = {
5 | prefixAgo: null,
6 | prefixFromNow: null,
7 | suffixAgo: '',
8 | suffixFromNow: '',
9 | seconds: '1 dəq',
10 | minute: '1 dəq',
11 | minutes: '%d dəq',
12 | hour: '1 saat',
13 | hours: '%d saat',
14 | day: '1 gün',
15 | days: '%d gün',
16 | month: '1 ay',
17 | months: '%d ay',
18 | year: '1 il',
19 | years: '%d il',
20 | wordSeparator: '',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/en-short.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // English shortened
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: '1m',
11 | minute: '1m',
12 | minutes: '%dm',
13 | hour: '1h',
14 | hours: '%dh',
15 | day: '1d',
16 | days: '%dd',
17 | month: '1mo',
18 | months: '%dmo',
19 | year: '1yr',
20 | years: '%dyr',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/es-short.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Spanish shortened
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: '1m',
11 | minute: '1m',
12 | minutes: '%dm',
13 | hour: '1h',
14 | hours: '%dh',
15 | day: '1d',
16 | days: '%dd',
17 | month: '1me',
18 | months: '%dme',
19 | year: '1a',
20 | years: '%da',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/it-short.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Italian shortened
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: '1m',
11 | minute: '1m',
12 | minutes: '%dm',
13 | hour: '1h',
14 | hours: '%dh',
15 | day: '1g',
16 | days: '%dg',
17 | month: '1me',
18 | months: '%dme',
19 | year: '1a',
20 | years: '%da',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/pt-short.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Portuguese shortened
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: '1m',
11 | minute: '1m',
12 | minutes: '%dm',
13 | hour: '1h',
14 | hours: '%dh',
15 | day: '1d',
16 | days: '%dd',
17 | month: '1M',
18 | months: '%dM',
19 | year: '1a',
20 | years: '%da',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/tr-short.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Turkish shortened
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: '1sn',
11 | minute: '1d',
12 | minutes: '%dd',
13 | hour: '1s',
14 | hours: '%ds',
15 | day: '1g',
16 | days: '%dg',
17 | month: '1ay',
18 | months: '%day',
19 | year: '1y',
20 | years: '%dy',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/de-short.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // German shortened
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: 's',
11 | minute: '1m',
12 | minutes: '%dm',
13 | hour: '1h',
14 | hours: '%dh',
15 | day: '1T.',
16 | days: '%dT.',
17 | month: '1Mt.',
18 | months: '%dMt.',
19 | year: '1J.',
20 | years: '%dJ.',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/it.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Italian
5 | const strings: L10nsStrings = {
6 | suffixAgo: 'fa',
7 | suffixFromNow: 'da ora',
8 | seconds: 'meno di un minuto',
9 | minute: 'circa un minuto',
10 | minutes: '%d minuti',
11 | hour: "circa un'ora",
12 | hours: 'circa %d ore',
13 | day: 'un giorno',
14 | days: '%d giorni',
15 | month: 'circa un mese',
16 | months: '%d mesi',
17 | year: 'circa un anno',
18 | years: '%d anni',
19 | }
20 |
21 | export default strings
22 |
--------------------------------------------------------------------------------
/src/language-strings/ka-short.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // English shortened
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: '1წმ',
11 | minute: '1წთ',
12 | minutes: '%dწთ',
13 | hour: '1სთ',
14 | hours: '%dსთ',
15 | day: '1დღე',
16 | days: '%dდღე',
17 | month: '1თვე',
18 | months: '%dთვე',
19 | year: '1წ',
20 | years: '%dწ',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/pt-br-short.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Portuguese Brasil shortened
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: '1m',
11 | minute: '1m',
12 | minutes: '%dm',
13 | hour: '1h',
14 | hours: '%dh',
15 | day: '1d',
16 | days: '%dd',
17 | month: '1M',
18 | months: '%dM',
19 | year: '1a',
20 | years: '%da',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/az.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | const strings: L10nsStrings = {
5 | prefixAgo: null,
6 | prefixFromNow: null,
7 | suffixAgo: 'əvvəl',
8 | suffixFromNow: 'sonra',
9 | seconds: 'saniyələr',
10 | minute: '1 dəqiqə',
11 | minutes: '%d dəqiqə',
12 | hour: '1 saat',
13 | hours: '%d saat',
14 | day: '1 gün',
15 | days: '%d gün',
16 | month: '1 ay',
17 | months: '%d ay',
18 | year: '1 il',
19 | years: '%d il',
20 | wordSeparator: '',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/en-fuzzy-short.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // English shortened
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: 'just now',
11 | minute: '1m',
12 | minutes: '%dm',
13 | hour: '1h',
14 | hours: '%dh',
15 | day: '1d',
16 | days: '%dd',
17 | month: '1mo',
18 | months: '%dmo',
19 | year: '1yr',
20 | years: '%dyr',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/hy.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Armenian
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: 'առաջ',
9 | suffixFromNow: 'հետո',
10 | seconds: 'վայրկյաններ',
11 | minute: 'մեկ րոպե',
12 | minutes: '%d րոպե',
13 | hour: 'մեկ ժամ',
14 | hours: '%d ժամ',
15 | day: 'մեկ օր',
16 | days: '%d օր',
17 | month: 'մեկ ամիս',
18 | months: '%d ամիս',
19 | year: 'մեկ տարի',
20 | years: '%d տարի',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/ja.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Japanese
5 | const strings: L10nsStrings = {
6 | prefixAgo: '',
7 | prefixFromNow: '今から',
8 | suffixAgo: '前',
9 | suffixFromNow: '後',
10 | seconds: '1 分未満',
11 | minute: '約 1 分',
12 | minutes: '%d 分',
13 | hour: '約 1 時間',
14 | hours: '約 %d 時間',
15 | day: '約 1 日',
16 | days: '約 %d 日',
17 | month: '約 1 ヶ月',
18 | months: '約 %d ヶ月',
19 | year: '約 1 年',
20 | years: '約 %d 年',
21 | wordSeparator: '',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/ro.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Romanian
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'acum',
7 | prefixFromNow: 'in timp de',
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: 'mai putin de un minut',
11 | minute: 'un minut',
12 | minutes: '%d minute',
13 | hour: 'o ora',
14 | hours: '%d ore',
15 | day: 'o zi',
16 | days: '%d zile',
17 | month: 'o luna',
18 | months: '%d luni',
19 | year: 'un an',
20 | years: '%d ani',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/zh-CN.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Simplified Chinese
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: '之前',
9 | suffixFromNow: '之后',
10 | seconds: '不到1分钟',
11 | minute: '大约1分钟',
12 | minutes: '%d分钟',
13 | hour: '大约1小时',
14 | hours: '大约%d小时',
15 | day: '1天',
16 | days: '%d天',
17 | month: '大约1个月',
18 | months: '%d月',
19 | year: '大约1年',
20 | years: '%d年',
21 |
22 | wordSeparator: '',
23 | }
24 |
25 | export default strings
26 |
--------------------------------------------------------------------------------
/src/language-strings/bg.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | const strings: L10nsStrings = {
5 | prefixAgo: 'преди',
6 | prefixFromNow: 'след',
7 | suffixAgo: null,
8 | suffixFromNow: null,
9 | seconds: 'по-малко от минута',
10 | minute: 'една минута',
11 | minutes: '%d минути',
12 | hour: 'един час',
13 | hours: '%d часа',
14 | day: 'един ден',
15 | days: '%d дни',
16 | month: 'един месец',
17 | months: '%d месеца',
18 | year: 'една година',
19 | years: '%d години',
20 | }
21 |
22 | export default strings
23 |
--------------------------------------------------------------------------------
/src/language-strings/mk.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Macedonian
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'пред',
7 | prefixFromNow: 'за',
8 | suffixAgo: null,
9 | suffixFromNow: null,
10 | seconds: '%d секунди',
11 | minute: '%d минута',
12 | minutes: '%d минути',
13 | hour: '%d час',
14 | hours: '%d часа',
15 | day: '%d ден',
16 | days: '%d денови',
17 | month: '%d месец',
18 | months: '%d месеци',
19 | year: '%d година',
20 | years: '%d години',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/gl.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Galician
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'hai',
7 | prefixFromNow: 'dentro de',
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: 'menos dun minuto',
11 | minute: 'un minuto',
12 | minutes: 'uns %d minutos',
13 | hour: 'unha hora',
14 | hours: '%d horas',
15 | day: 'un día',
16 | days: '%d días',
17 | month: 'un mes',
18 | months: '%d meses',
19 | year: 'un ano',
20 | years: '%d anos',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/zh-TW.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Traditional Chinese, zh-tw
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: '之前',
9 | suffixFromNow: '之後',
10 | seconds: '不到1分鐘',
11 | minute: '大約1分鐘',
12 | minutes: '%d分鐘',
13 | hour: '大約1小時',
14 | hours: '%d小時',
15 | day: '大約1天',
16 | days: '%d天',
17 | month: '大約1個月',
18 | months: '%d個月',
19 | year: '大約1年',
20 | years: '%d年',
21 |
22 | wordSeparator: '',
23 | }
24 |
25 | export default strings
26 |
--------------------------------------------------------------------------------
/src/language-strings/es.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Spanish
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'hace',
7 | prefixFromNow: 'dentro de',
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: 'menos de un minuto',
11 | minute: 'un minuto',
12 | minutes: 'unos %d minutos',
13 | hour: 'una hora',
14 | hours: '%d horas',
15 | day: 'un día',
16 | days: '%d días',
17 | month: 'un mes',
18 | months: '%d meses',
19 | year: 'un año',
20 | years: '%d años',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/lt.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Lithuanian
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'prieš',
7 | prefixFromNow: null,
8 | suffixAgo: null,
9 | suffixFromNow: 'nuo dabar',
10 | seconds: '%d sek.',
11 | minute: 'min.',
12 | minutes: '%d min.',
13 | hour: 'val.',
14 | hours: '%d val.',
15 | day: '1 d.',
16 | days: '%d d.',
17 | month: 'mėn.',
18 | months: '%d mėn.',
19 | year: 'metus',
20 | years: '%d metus',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/lv.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Latvian
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'pirms',
7 | prefixFromNow: null,
8 | suffixAgo: null,
9 | suffixFromNow: 'no šī brīža',
10 | seconds: '%d sek.',
11 | minute: 'min.',
12 | minutes: '%d min.',
13 | hour: 'st.',
14 | hours: '%d st.',
15 | day: '1 d.',
16 | days: '%d d.',
17 | month: 'mēnesis.',
18 | months: '%d mēnesis.',
19 | year: 'gads',
20 | years: '%d gads',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/pt-br.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Brazilian Portuguese
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'há',
7 | prefixFromNow: 'em',
8 | suffixAgo: null,
9 | suffixFromNow: null,
10 | seconds: 'alguns segundos',
11 | minute: 'um minuto',
12 | minutes: '%d minutos',
13 | hour: 'uma hora',
14 | hours: '%d horas',
15 | day: 'um dia',
16 | days: '%d dias',
17 | month: 'um mês',
18 | months: '%d meses',
19 | year: 'um ano',
20 | years: '%d anos',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/sk.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Slovak
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'pred',
7 | prefixFromNow: null,
8 | suffixAgo: null,
9 | suffixFromNow: null,
10 | seconds: 'menej než minútou',
11 | minute: 'minútou',
12 | minutes: '%d minútami',
13 | hour: 'hodinou',
14 | hours: '%d hodinami',
15 | day: '1 dňom',
16 | days: '%d dňami',
17 | month: '1 mesiacom',
18 | months: '%d mesiacmi',
19 | year: '1 rokom',
20 | years: '%d rokmi',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/ko.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Korean
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: '전',
9 | suffixFromNow: '후',
10 | seconds: '몇 초',
11 | minute: '약 1분',
12 | minutes: '%d분',
13 | hour: '약 1시간',
14 | hours: '약 %d시간',
15 | day: '하루',
16 | days: '%d일',
17 | week: '약 1주',
18 | weeks: '%d주',
19 | month: '약 1개월',
20 | months: '%d개월',
21 | year: '약 1년',
22 | years: '%d년',
23 | wordSeparator: ' ',
24 | }
25 |
26 | export default strings
27 |
--------------------------------------------------------------------------------
/src/language-strings/pt.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Portuguese
5 | const strings: L10nsStrings = {
6 | suffixAgo: 'atrás',
7 | suffixFromNow: 'a partir de agora',
8 | seconds: 'menos de um minuto',
9 | minute: 'cerca de um minuto',
10 | minutes: '%d minutos',
11 | hour: 'cerca de uma hora',
12 | hours: 'cerca de %d horas',
13 | day: 'um dia',
14 | days: '%d dias',
15 | month: 'cerca de um mês',
16 | months: '%d meses',
17 | year: 'cerca de um ano',
18 | years: '%d anos',
19 | }
20 |
21 | export default strings
22 |
--------------------------------------------------------------------------------
/src/language-strings/uz.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Uzbek
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: 'keyin',
8 | suffixAgo: 'avval',
9 | suffixFromNow: null,
10 | seconds: 'bir necha soniya',
11 | minute: '1 daqiqa',
12 | minutes: '%d daqiqa',
13 | hour: '1 soat',
14 | hours: '%d soat',
15 | day: '1 kun',
16 | days: '%d kun',
17 | month: '1 oy',
18 | months: '%d oy',
19 | year: '1 yil',
20 | years: '%d yil',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/da.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Danish
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'for',
7 | prefixFromNow: 'om',
8 | suffixAgo: 'siden',
9 | suffixFromNow: '',
10 | seconds: 'mindre end et minut',
11 | minute: 'ca. et minut',
12 | minutes: '%d minutter',
13 | hour: 'ca. en time',
14 | hours: 'ca. %d timer',
15 | day: 'en dag',
16 | days: '%d dage',
17 | month: 'ca. en måned',
18 | months: '%d måneder',
19 | year: 'ca. et år',
20 | years: '%d år',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/eu.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | const strings: L10nsStrings = {
5 | prefixAgo: 'duela',
6 | prefixFromNow: 'hemendik',
7 | suffixAgo: '',
8 | suffixFromNow: 'barru',
9 | seconds: 'minutu bat bainu gutxiago',
10 | minute: 'minutu bat',
11 | minutes: '%d minutu inguru',
12 | hour: 'ordu bat',
13 | hours: '%d ordu',
14 | day: 'egun bat',
15 | days: '%d egun',
16 | month: 'hilabete bat',
17 | months: '%d hilabete',
18 | year: 'urte bat',
19 | years: '%d urte',
20 | }
21 |
22 | export default strings
23 |
--------------------------------------------------------------------------------
/src/language-strings/is.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | const strings: L10nsStrings = {
5 | prefixAgo: 'fyrir',
6 | prefixFromNow: 'eftir',
7 | suffixAgo: 'síðan',
8 | suffixFromNow: null,
9 | seconds: 'minna en mínútu',
10 | minute: 'mínútu',
11 | minutes: '%d mínútum',
12 | hour: 'klukkutíma',
13 | hours: 'um %d klukkutímum',
14 | day: 'degi',
15 | days: '%d dögum',
16 | month: 'mánuði',
17 | months: '%d mánuðum',
18 | year: 'ári',
19 | years: '%d árum',
20 | wordSeparator: ' ',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/so.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Somali
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: 'kahor',
9 | suffixFromNow: 'kadib',
10 | seconds: 'sikino',
11 | minute: 'daqiiqad',
12 | minutes: '%d daqiiqo',
13 | hour: 'saac',
14 | hours: '%d saacadood',
15 | day: 'maalin',
16 | days: '%d maalmood',
17 | month: 'bil',
18 | months: '%d bilood',
19 | year: 'sano',
20 | years: '%d sano',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/no.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Norwegian
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'for',
7 | prefixFromNow: 'om',
8 | suffixAgo: 'siden',
9 | suffixFromNow: '',
10 | seconds: 'mindre enn et minutt',
11 | minute: 'ca. et minutt',
12 | minutes: '%d minutter',
13 | hour: 'ca. en time',
14 | hours: 'ca. %d timer',
15 | day: 'en dag',
16 | days: '%d dager',
17 | month: 'ca. en måned',
18 | months: '%d måneder',
19 | year: 'ca. et år',
20 | years: '%d år',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/dateParser.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export default function dateParser(date: string | number | Date): Date {
4 | const parsed = new Date(date)
5 | if (!Number.isNaN(parsed.valueOf())) {
6 | return parsed
7 | }
8 |
9 | const parts: ?$ReadOnlyArray = String(date).match(/\d+/g)
10 | if (parts == null || parts.length <= 2) {
11 | return parsed
12 | } else {
13 | const [firstP, secondP, ...restPs] = parts.map((x) => parseInt(x))
14 | const correctedParts = [firstP, secondP - 1, ...restPs]
15 | const isoDate = new Date(Date.UTC(...correctedParts))
16 | return isoDate
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/language-strings/af.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | const strings: L10nsStrings = {
5 | prefixAgo: null,
6 | prefixFromNow: null,
7 | suffixAgo: 'gelede',
8 | suffixFromNow: 'van nou af',
9 | second: '1 sekonde',
10 | seconds: '%d sekondes',
11 | minute: '1 minuut',
12 | minutes: '%d minute',
13 | hour: '1 uur',
14 | hours: '%d ure',
15 | day: '1 dag',
16 | days: '%d dae',
17 | month: '1 maand',
18 | months: '%d maande',
19 | year: '1 jaar',
20 | years: '%d jaar',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/ca.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Catalan
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'fa',
7 | prefixFromNow: "d'aqui a",
8 | suffixAgo: null,
9 | suffixFromNow: null,
10 | seconds: "menys d'1 minut",
11 | minute: '1 minut',
12 | minutes: 'uns %d minuts',
13 | hour: '1 hora',
14 | hours: 'unes %d hores',
15 | day: '1 dia',
16 | days: '%d dies',
17 | month: 'aproximadament un mes',
18 | months: '%d mesos',
19 | year: 'aproximadament un any',
20 | years: '%d anys',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/cy.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Welsh
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: 'yn ôl',
9 | suffixFromNow: 'o hyn',
10 | seconds: 'llai na munud',
11 | minute: 'am funud',
12 | minutes: '%d munud',
13 | hour: 'tua awr',
14 | hours: 'am %d awr',
15 | day: 'y dydd',
16 | days: '%d diwrnod',
17 | month: 'tua mis',
18 | months: '%d mis',
19 | year: 'am y flwyddyn',
20 | years: '%d blynedd',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/rw.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Kinyarwanda
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'hashize',
7 | prefixFromNow: 'mu',
8 | suffixAgo: null,
9 | suffixFromNow: null,
10 | seconds: 'amasegonda macye',
11 | minute: 'umunota',
12 | minutes: 'iminota %d',
13 | hour: 'isaha',
14 | hours: 'amasaha %d',
15 | day: 'umunsi',
16 | days: 'iminsi %d',
17 | month: 'ukwezi',
18 | months: 'amezi %d',
19 | year: 'umwaka',
20 | years: 'imyaka %d',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/oc.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Catalan
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'fa',
7 | prefixFromNow: "d’aqui",
8 | suffixAgo: null,
9 | suffixFromNow: null,
10 | seconds: "mens d'1 minuta",
11 | minute: '1 minuta',
12 | minutes: 'unas %d minutas',
13 | hour: '1 ora',
14 | hours: 'unas %d oras',
15 | day: '1 jorn',
16 | days: '%d jorns',
17 | month: 'aproximativament un mes',
18 | months: '%d meses',
19 | year: 'aproximativament un an',
20 | years: '%d ans',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/de.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // German
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'vor',
7 | prefixFromNow: 'in',
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: 'wenigen Sekunden',
11 | minute: 'etwa einer Minute',
12 | minutes: '%d Minuten',
13 | hour: 'etwa einer Stunde',
14 | hours: '%d Stunden',
15 | day: 'etwa einem Tag',
16 | days: '%d Tagen',
17 | month: 'etwa einem Monat',
18 | months: '%d Monaten',
19 | year: 'etwa einem Jahr',
20 | years: '%d Jahren',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/el.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Greek
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'πριν',
7 | prefixFromNow: 'σε',
8 | suffixAgo: '',
9 | suffixFromNow: '',
10 | seconds: 'λιγότερο από ένα λεπτό',
11 | minute: 'περίπου ένα λεπτό',
12 | minutes: '%d λεπτά',
13 | hour: 'περίπου μία ώρα',
14 | hours: 'περίπου %d ώρες',
15 | day: 'μία μέρα',
16 | days: '%d μέρες',
17 | month: 'περίπου ένα μήνα',
18 | months: '%d μήνες',
19 | year: 'περίπου ένα χρόνο',
20 | years: '%d χρόνια',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/hi.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Hindi (Template)
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: 'अब से',
8 | suffixAgo: 'पहले',
9 | suffixFromNow: 'मेे',
10 | seconds: 'एक मिनट से कम',
11 | minute: 'मिनट',
12 | minutes: '%d मिनट',
13 | hour: 'करीब एक घंटा',
14 | hours: 'करीब %d घंटे',
15 | day: 'दिन',
16 | days: '%d दिन',
17 | month: 'तक़रीबन एक महीना',
18 | months: '%d महीने',
19 | year: 'लगभग एक साल',
20 | years: '%d साल',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/si.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Sinhalese (SI)
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: 'පෙර',
9 | suffixFromNow: 'පසුව',
10 | seconds: 'තත්පර කිහිපයකට',
11 | minute: 'මිනිත්තුවකට පමණ',
12 | minutes: 'මිනිත්තු %d කට',
13 | hour: 'පැයක් පමණ ',
14 | hours: 'පැය %d කට පමණ',
15 | day: 'දවසක ට',
16 | days: 'දවස් %d කට ',
17 | month: 'මාසයක් පමණ',
18 | months: 'මාස %d කට',
19 | year: 'වසරක් පමණ',
20 | years: 'වසරක් %d කට පමණ',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/sv.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Swedish
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'för',
7 | prefixFromNow: 'om',
8 | suffixAgo: 'sedan',
9 | suffixFromNow: '',
10 | seconds: 'mindre än en minut',
11 | minute: 'ungefär en minut',
12 | minutes: '%d minuter',
13 | hour: 'ungefär en timme',
14 | hours: 'ungefär %d timmar',
15 | day: 'en dag',
16 | days: '%d dagar',
17 | month: 'ungefär en månad',
18 | months: '%d månader',
19 | year: 'ungefär ett år',
20 | years: '%d år',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/fr.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // French
5 | const strings: L10nsStrings = {
6 | // environ ~= about, it's optional
7 | prefixAgo: 'il y a',
8 | prefixFromNow: "d'ici",
9 | seconds: "moins d'une minute",
10 | minute: 'environ une minute',
11 | minutes: 'environ %d minutes',
12 | hour: 'environ une heure',
13 | hours: 'environ %d heures',
14 | day: 'environ un jour',
15 | days: 'environ %d jours',
16 | month: 'environ un mois',
17 | months: 'environ %d mois',
18 | year: 'un an',
19 | years: '%d ans',
20 | }
21 |
22 | export default strings
23 |
--------------------------------------------------------------------------------
/src/language-strings/id.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Indonesian
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: 'yang lalu',
9 | suffixFromNow: 'dari sekarang',
10 | seconds: 'kurang dari semenit',
11 | minute: 'sekitar satu menit',
12 | minutes: '%d menit',
13 | hour: 'sekitar sejam',
14 | hours: 'sekitar %d jam',
15 | day: 'sehari',
16 | days: '%d hari',
17 | month: 'sekitar sebulan',
18 | months: '%d bulan',
19 | year: 'sekitar setahun',
20 | years: '%d tahun',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/vi.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Vietnamese
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'cách đây',
7 | prefixFromNow: null,
8 | suffixAgo: null,
9 | suffixFromNow: 'sau',
10 | seconds: 'chưa đến 1 phút',
11 | minute: 'khoảng 1 phút',
12 | minutes: '%d phút',
13 | hour: 'khoảng 1 tiếng',
14 | hours: 'khoảng %d tiếng',
15 | day: '1 ngày',
16 | days: '%d ngày',
17 | month: 'khoảng 1 tháng',
18 | months: '%d tháng',
19 | year: 'khoảng 1 năm',
20 | years: '%d năm',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/en-fuzzy.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // English (Template)
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: 'ago',
9 | suffixFromNow: 'from now',
10 | seconds: 'a moment',
11 | minute: 'about a minute',
12 | minutes: '%d minutes',
13 | hour: 'about an hour',
14 | hours: 'about %d hours',
15 | day: 'a day',
16 | days: '%d days',
17 | month: 'about a month',
18 | months: '%d months',
19 | year: 'about a year',
20 | years: '%d years',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/th.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Thai
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: 'ที่แล้ว',
9 | suffixFromNow: 'จากตอนนี้',
10 | seconds: 'น้อยกว่าหนึ่งนาที',
11 | minute: 'ประมาณหนึ่งนาที',
12 | minutes: '%d นาที',
13 | hour: 'ประมาณหนึ่งชั่วโมง',
14 | hours: 'ประมาณ %d ชั่วโมง',
15 | day: 'หนึ่งวัน',
16 | days: '%d วัน',
17 | month: 'ประมาณหนึ่งเดือน',
18 | months: '%d เดือน',
19 | year: 'ประมาณหนึ่งปี',
20 | years: '%d ปี',
21 | wordSeparator: '',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/en.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // English (Template)
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: 'ago',
9 | suffixFromNow: 'from now',
10 | seconds: 'less than a minute',
11 | minute: 'about a minute',
12 | minutes: '%d minutes',
13 | hour: 'about an hour',
14 | hours: 'about %d hours',
15 | day: 'a day',
16 | days: '%d days',
17 | month: 'about a month',
18 | months: '%d months',
19 | year: 'about a year',
20 | years: '%d years',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/hu.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Hungarian
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: null,
9 | suffixFromNow: null,
10 | seconds: 'kevesebb mint egy perce',
11 | minute: 'körülbelül egy perce',
12 | minutes: '%d perce',
13 | hour: 'körülbelül egy órája',
14 | hours: 'körülbelül %d órája',
15 | day: 'körülbelül egy napja',
16 | days: '%d napja',
17 | month: 'körülbelül egy hónapja',
18 | months: '%d hónapja',
19 | year: 'körülbelül egy éve',
20 | years: '%d éve',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/nl.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Dutch
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: 'over',
8 | suffixAgo: 'geleden',
9 | suffixFromNow: null,
10 | seconds: 'minder dan een minuut',
11 | minute: 'ongeveer een minuut',
12 | minutes: '%d minuten',
13 | hour: 'ongeveer een uur',
14 | hours: 'ongeveer %d uur',
15 | day: 'een dag',
16 | days: '%d dagen',
17 | month: 'ongeveer een maand',
18 | months: '%d maanden',
19 | year: 'ongeveer een jaar',
20 | years: '%d jaar',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/jv.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Javanesse (Boso Jowo)
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: 'kepungkur',
9 | suffixFromNow: 'seko saiki',
10 | seconds: 'kurang seko sakmenit',
11 | minute: 'kurang luwih sakmenit',
12 | minutes: '%d menit',
13 | hour: 'kurang luwih sakjam',
14 | hours: 'kurang luwih %d jam',
15 | day: 'sedina',
16 | days: '%d dina',
17 | month: 'kurang luwih sewulan',
18 | months: '%d wulan',
19 | year: 'kurang luwih setahun',
20 | years: '%d tahun',
21 | }
22 |
23 | export default strings
24 |
--------------------------------------------------------------------------------
/src/language-strings/ka.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // English (Template)
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: 'წინ',
9 | suffixFromNow: 'შემდეგ',
10 | seconds: 'რამიდენიმე წამის',
11 | minute: 'დაახლოებით ერთი წუთის',
12 | minutes: '%d წუთის',
13 | hour: 'დაახლოებით ერთი საათის',
14 | hours: '%d საათის',
15 | day: 'ერთი დღის',
16 | days: '%d დღის',
17 | month: 'დაახლოებით ერთი თვის',
18 | months: '%d თვის',
19 | year: 'დაახლოებით ერთი თვის',
20 | years: '%d წლის',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/src/language-strings/ta.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // English (Template)
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: 'முன்பு',
9 | suffixFromNow: 'இப்போது முதல்',
10 | seconds: 'ஒரு நிமிடத்திற்கும் குறைவாக',
11 | minute: 'ஒரு நிமிடம்',
12 | minutes: '%d நிமிடங்கள்',
13 | hour: 'சுமார் ஒரு மணிநேரம்',
14 | hours: 'சுமார் %d மணிநேரம் ',
15 | day: 'ஒரு நாள்',
16 | days: '%d நாட்கள்',
17 | month: 'சுமார் ஒரு மாதம்',
18 | months: '%d மாதங்கள்',
19 | year: 'சுமார் ஒரு வருடம்',
20 | years: '%d ஆண்டுகள்',
21 | wordSeparator: ' ',
22 | }
23 |
24 | export default strings
25 |
--------------------------------------------------------------------------------
/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line
2 | import commonjs from '@rollup/plugin-commonjs'
3 | import { babel } from '@rollup/plugin-babel'
4 | import resolve from '@rollup/plugin-node-resolve'
5 | import replace from '@rollup/plugin-replace'
6 |
7 | export default {
8 | external: [],
9 | input: 'examples/simple/index.js',
10 | output: {
11 | file: 'examples/simple/bundle.js',
12 | format: 'iife',
13 | },
14 | plugins: [
15 | babel({ babelHelpers: 'bundled' }),
16 | commonjs(),
17 | resolve({
18 | browser: true,
19 | dedupe: ['react', 'react-dom'],
20 | }),
21 | replace({
22 | 'process.env.NODE_ENV': JSON.stringify('production'),
23 | }),
24 | ],
25 | }
26 |
--------------------------------------------------------------------------------
/src/language-strings/dv.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | /**
5 | * Dhivehi time in Thaana for timeago.js
6 | **/
7 | const strings: L10nsStrings = {
8 | prefixAgo: null,
9 | prefixFromNow: null,
10 | suffixAgo: 'ކުރިން',
11 | suffixFromNow: 'ފަހުން',
12 | seconds: 'ސިކުންތުކޮޅެއް',
13 | minute: 'މިނިޓެއްވަރު',
14 | minutes: '%d މިނިޓު',
15 | hour: 'ގަޑިއެއްވަރު',
16 | hours: 'ގާތްގަނޑަކަށް %d ގަޑިއިރު',
17 | day: 'އެއް ދުވަސް',
18 | days: 'މީގެ %d ދުވަސް',
19 | month: 'މަހެއްވަރު',
20 | months: 'މީގެ %d މަސް',
21 | year: 'އަހަރެއްވަރު',
22 | years: 'މީގެ %d އަހަރު',
23 | wordSeparator: ' ',
24 | }
25 |
26 | export default strings
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # Compiled binary addons (http://nodejs.org/api/addons.html)
20 | build/Release
21 |
22 | # Dependency directory
23 | # Deployed apps should consider commenting this line out:
24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
25 |
26 | node_modules
27 | lib
28 | es6
29 | examples/simple/bundle.js
30 |
31 | .DS_Store
32 | */.DS_Store
33 |
--------------------------------------------------------------------------------
/flow-typed/npm/flow-enums-runtime_v0.x.x.js:
--------------------------------------------------------------------------------
1 | // flow-typed signature: 619aca2b81b5b5d287785c449228cc8a
2 | // flow-typed version: ca06757594/flow-enums-runtime_v0.x.x/flow_>=v0.83.x
3 |
4 | declare module 'flow-enums-runtime' {
5 | declare type Members = { +[key: string]: string };
6 |
7 | declare type EnumPrototype = {|
8 | isValid: (x: $Keys) => boolean,
9 | cast: (x: V) => V | void,
10 | members: () => Array<$Keys>,
11 | getName: (value: string) => string,
12 | |};
13 |
14 | declare type Enum = {|
15 | (members?: T): {| ...EnumPrototype, ...T |},
16 | Mirrored: (members: Array<$Keys>) => {| ...Members, ...EnumPrototype |},
17 | |};
18 |
19 | declare module.exports: Enum;
20 | }
21 |
--------------------------------------------------------------------------------
/src/language-strings/he.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Hebrew
5 | const strings: L10nsStrings = {
6 | prefixAgo: 'לפני',
7 | prefixFromNow: 'עוד',
8 | seconds: 'פחות מדקה',
9 | minute: 'דקה',
10 | minutes: '%d דקות',
11 | hour: 'שעה',
12 | hours: function (number) {
13 | return number === 2 ? 'שעתיים' : '%d שעות'
14 | },
15 | day: 'יום',
16 | days: function (number) {
17 | return number === 2 ? 'יומיים' : '%d ימים'
18 | },
19 | month: 'חודש',
20 | months: function (number) {
21 | return number === 2 ? 'חודשיים' : '%d חודשים'
22 | },
23 | year: 'שנה',
24 | years: function (number) {
25 | return number === 2 ? 'שנתיים' : '%d שנים'
26 | },
27 | }
28 |
29 | export default strings
30 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # Compiled binary addons (http://nodejs.org/api/addons.html)
20 | build/Release
21 |
22 | # Dependency directory
23 | # Deployed apps should consider commenting this line out:
24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
25 |
26 | node_modules
27 | lib
28 | es6
29 | examples/simple/bundle.js
30 |
31 | .DS_Store
32 | */.DS_Store
33 | yarn-error.log
34 |
35 | # Files not in .gitignore
36 | .babelrc
37 | .circleci
38 | CHANGELOG.md
39 | examples/simple/index.html
40 | package.json
41 | README.md
--------------------------------------------------------------------------------
/__tests__/formatter-value-tests.js:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom'
2 | import { render, screen } from '@testing-library/react'
3 | import React from 'react'
4 | import TimeAgo from '../src'
5 |
6 | test('1 minute ago', () => {
7 | render()
8 | expect(screen.getByText('1 minute ago')).toBeInTheDocument()
9 | })
10 |
11 | it('should handle null formatter gracefully', () => {
12 | render()
13 | expect(screen.getByText('1 minute ago')).toBeInTheDocument()
14 | })
15 |
16 | it('should handle empty function formatter', () => {
17 | render( {}} />)
18 | expect(screen.getByText('2 hours ago')).toBeInTheDocument()
19 | })
20 |
21 | it('should handle formatter throwing an error', () => {
22 | render(
23 | {
26 | throw new Error('Faulty formatter')
27 | }}
28 | />,
29 | )
30 | expect(screen.getByText('2 minutes ago')).toBeInTheDocument()
31 | })
32 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "eslint:recommended",
4 | "plugin:ft-flow/recommended",
5 | "plugin:react-hooks/recommended",
6 | "prettier"
7 | ],
8 | "plugins": [
9 | "ft-flow",
10 | "react-hooks"
11 | ],
12 | "parser": "hermes-eslint",
13 | "rules": {
14 | "curly": ["error", "multi-line"],
15 | "linebreak-style": ["error", "unix"],
16 | "max-len": [
17 | "error",
18 | {
19 | "code": 80,
20 | "ignoreComments": true,
21 | "ignoreStrings": true,
22 | "ignoreTemplateLiterals": true
23 | }
24 | ],
25 | "new-cap": "off",
26 | "no-case-declarations": "error",
27 | "no-var": "error",
28 | "prefer-const": "error",
29 | "no-unused-vars": [
30 | "error",
31 | {
32 | "varsIgnorePattern": "^_",
33 | "argsIgnorePattern": "^_"
34 | }
35 | ]
36 | },
37 | "env": {
38 | "node": true,
39 | "es6": true
40 | },
41 | "ignorePatterns": [
42 | "__tests__",
43 | "node_modules",
44 | "coverage",
45 | "examples",
46 | "lib",
47 | "es6"
48 | ]
49 | }
--------------------------------------------------------------------------------
/src/language-strings/pl.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Polish
5 | function numpf(n: number, s: string, t: string): string {
6 | // s - 2-4, 22-24, 32-34 ...
7 | // t - 5-21, 25-31, ...
8 | const n10 = n % 10
9 | if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) {
10 | return s
11 | } else {
12 | return t
13 | }
14 | }
15 |
16 | const strings: L10nsStrings = {
17 | prefixAgo: null,
18 | prefixFromNow: 'za',
19 | suffixAgo: 'temu',
20 | suffixFromNow: null,
21 | seconds: 'mniej niż minutę',
22 | minute: 'minutę',
23 | minutes: function (value) {
24 | return numpf(value, '%d minuty', '%d minut')
25 | },
26 | hour: 'godzinę',
27 | hours: function (value) {
28 | return numpf(value, '%d godziny', '%d godzin')
29 | },
30 | day: 'dzień',
31 | days: '%d dni',
32 | month: 'miesiąc',
33 | months: function (value) {
34 | return numpf(value, '%d miesiące', '%d miesięcy')
35 | },
36 | year: 'rok',
37 | years: function (value) {
38 | return numpf(value, '%d lata', '%d lat')
39 | },
40 | }
41 |
42 | export default strings
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Naman Goel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/language-strings/fi.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Finnish
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: 'sitten',
9 | suffixFromNow: 'tulevaisuudessa',
10 | seconds: 'alle minuutti',
11 | minute: 'minuutti',
12 | minutes: '%d minuuttia',
13 | hour: 'tunti',
14 | hours: '%d tuntia',
15 | day: 'päivä',
16 | days: '%d päivää',
17 | month: 'kuukausi',
18 | months: '%d kuukautta',
19 | year: 'vuosi',
20 | years: '%d vuotta',
21 | }
22 |
23 | export default strings
24 |
25 | // The above is not a great localization because one would usually
26 | // write "2 days ago" in Finnish as "2 päivää sitten", however
27 | // one would write "2 days into the future" as "2:n päivän päästä"
28 | // which cannot be achieved with localization support this simple.
29 | // This is because Finnish has word suffixes (attached directly
30 | // to the end of the word). The word "day" is "päivä" in Finnish.
31 | // As workaround, the above localizations will say
32 | // "2 päivää tulevaisuudessa" which is understandable but
33 | // not as fluent.
34 |
--------------------------------------------------------------------------------
/src/defaultFormatter.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import type { Formatter, Suffix, Unit } from './types'
4 |
5 | const defaultFormatter: Formatter = (
6 | value: number,
7 | _unit: Unit,
8 | suffix: string,
9 | ): string => {
10 | const unit = value !== 1 ? _unit + 's' : _unit
11 | return value + ' ' + unit + ' ' + suffix
12 | }
13 |
14 | export default defaultFormatter
15 |
16 | export type IntlFormatterOptions = $ReadOnly<{
17 | locale?: void | string,
18 | localeMatcher?: 'lookup' | 'best fit',
19 | numberingSystem?: Intl$NumberingSystem,
20 | style?: 'long' | 'short' | 'narrow',
21 | numeric?: 'always' | 'auto',
22 | }>
23 |
24 | export const makeIntlFormatter: (IntlFormatterOptions) => Formatter =
25 | ({ locale, ...options }) =>
26 | (value, unit, suffix) => {
27 | const RelativeTimeFormat = Intl.RelativeTimeFormat
28 | if (!RelativeTimeFormat) {
29 | throw new Error('Intl.RelativeTimeFormat is not supported')
30 | }
31 |
32 | const rtf = new RelativeTimeFormat(locale ?? undefined, {
33 | style: 'long',
34 | numeric: 'auto',
35 | ...options,
36 | })
37 |
38 | return rtf.format(suffix === 'ago' ? -value : value, unit)
39 | }
40 |
--------------------------------------------------------------------------------
/src/language-strings/et.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Estonian
5 | const strings: L10nsStrings = {
6 | prefixAgo: null,
7 | prefixFromNow: null,
8 | suffixAgo: 'tagasi',
9 | suffixFromNow: 'pärast',
10 | seconds: function (n, d) {
11 | return d < 0 ? 'vähem kui minuti aja' : 'vähem kui minut aega'
12 | },
13 | minute: function (n, d) {
14 | return d < 0 ? 'umbes minuti aja' : 'umbes minut aega'
15 | },
16 | minutes: function (n, d) {
17 | return d < 0 ? '%d minuti' : '%d minutit'
18 | },
19 | hour: function (n, d) {
20 | return d < 0 ? 'umbes tunni aja' : 'umbes tund aega'
21 | },
22 | hours: function (n, d) {
23 | return d < 0 ? '%d tunni' : '%d tundi'
24 | },
25 | day: function (n, d) {
26 | return d < 0 ? 'umbes päeva' : 'umbes päev'
27 | },
28 | days: '%d päeva',
29 | month: function (n, d) {
30 | return d < 0 ? 'umbes kuu aja' : 'umbes kuu aega'
31 | },
32 | months: function (n, d) {
33 | return d < 0 ? '%d kuu' : '%d kuud'
34 | },
35 | year: function (n, d) {
36 | return d < 0 ? 'umbes aasta aja' : 'umbes aasta aega'
37 | },
38 | years: function (n, d) {
39 | return d < 0 ? '%d aasta' : '%d aastat'
40 | },
41 | }
42 |
43 | export default strings
44 |
--------------------------------------------------------------------------------
/src/language-strings/ky.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Russian
5 | function numpf(n: number, f: string, s: string, t: string): string {
6 | // f - 1, 21, 31, ...
7 | // s - 2-4, 22-24, 32-34 ...
8 | // t - 5-20, 25-30, ...
9 | const n10 = n % 10
10 | if (n10 === 1 && (n === 1 || n > 20)) {
11 | return f
12 | } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) {
13 | return s
14 | } else {
15 | return t
16 | }
17 | }
18 |
19 | const strings: L10nsStrings = {
20 | prefixAgo: null,
21 | prefixFromNow: 'через',
22 | suffixAgo: 'мурун',
23 | suffixFromNow: null,
24 | seconds: '1 минуттан аз',
25 | minute: 'минута',
26 | minutes: function (value) {
27 | return numpf(value, '%d минута', '%d минута', '%d минут')
28 | },
29 | hour: 'саат',
30 | hours: function (value) {
31 | return numpf(value, '%d саат', '%d саат', '%d саат')
32 | },
33 | day: 'күн',
34 | days: function (value) {
35 | return numpf(value, '%d күн', '%d күн', '%d күн')
36 | },
37 | month: 'ай',
38 | months: function (value) {
39 | return numpf(value, '%d ай', '%d ай', '%d ай')
40 | },
41 | year: 'жыл',
42 | years: function (value) {
43 | return numpf(value, '%d жыл', '%d жыл', '%d жыл')
44 | },
45 | }
46 |
47 | export default strings
48 |
--------------------------------------------------------------------------------
/src/language-strings/fa-short.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | const hasIntl = typeof Intl !== 'undefined' && Intl.NumberFormat != null
5 |
6 | // persion shortened
7 | const strings: L10nsStrings = {
8 | prefixAgo: null,
9 | prefixFromNow: null,
10 | suffixAgo: '',
11 | suffixFromNow: '',
12 | seconds: '۱دقیقه',
13 | minute: '۱دقیقه',
14 | minutes: (value) => {
15 | if (hasIntl) {
16 | return new Intl.NumberFormat('fa').format(value) + 'دقیقه'
17 | }
18 | return '%dدقیقه'
19 | },
20 | hour: '۱ساعت',
21 | hours: (value) => {
22 | if (hasIntl) {
23 | return new Intl.NumberFormat('fa').format(value) + 'ساعت'
24 | }
25 | return '%dساعت'
26 | },
27 | day: '۱روز',
28 | days: (value) => {
29 | if (hasIntl) {
30 | return new Intl.NumberFormat('fa').format(value) + 'روز'
31 | }
32 | return '%dروز'
33 | },
34 | month: '۱ماه',
35 | months: (value) => {
36 | if (hasIntl) {
37 | return new Intl.NumberFormat('fa').format(value) + 'ماه'
38 | }
39 | return '%dماه'
40 | },
41 | year: '۱سال',
42 | years: (value) => {
43 | if (hasIntl) {
44 | return new Intl.NumberFormat('fa').format(value) + 'سال'
45 | }
46 | return '%dسال'
47 | },
48 | wordSeparator: ' ',
49 | }
50 |
51 | export default strings
52 |
--------------------------------------------------------------------------------
/src/language-strings/be.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Belarusian
5 | function numpf(n: number, f: string, s: string, t: string): string {
6 | // f - 1, 21, 31, ...
7 | // s - 2-4, 22-24, 32-34 ...
8 | // t - 5-20, 25-30, ...
9 | const n10 = n % 10
10 | if (n10 === 1 && (n === 1 || n > 20)) {
11 | return f
12 | } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) {
13 | return s
14 | } else {
15 | return t
16 | }
17 | }
18 |
19 | const strings: L10nsStrings = {
20 | prefixAgo: null,
21 | prefixFromNow: 'праз',
22 | suffixAgo: 'таму',
23 | suffixFromNow: null,
24 | seconds: 'менш хвіліны',
25 | minute: 'хвіліну',
26 | minutes: function (value) {
27 | return numpf(value, '%d хвіліна', '%d хвіліны', '%d хвілін')
28 | },
29 | hour: 'гадзіну',
30 | hours: function (value) {
31 | return numpf(value, '%d гадзіна', '%d гадзіны', '%d гадзін')
32 | },
33 | day: 'дзень',
34 | days: function (value) {
35 | return numpf(value, '%d дзень', '%d дні', '%d дзён')
36 | },
37 | month: 'месяц',
38 | months: function (value) {
39 | return numpf(value, '%d месяц', '%d месяцы', '%d месяцаў')
40 | },
41 | year: 'год',
42 | years: function (value) {
43 | return numpf(value, '%d год', '%d гады', '%d гадоў')
44 | },
45 | }
46 |
47 | export default strings
48 |
--------------------------------------------------------------------------------
/src/language-strings/uk.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Ukrainian
5 | function numpf(n: number, f: string, s: string, t: string): string {
6 | // f - 1, 21, 31, ...
7 | // s - 2-4, 22-24, 32-34 ...
8 | // t - 5-20, 25-30, ...
9 | const n10 = n % 10
10 | if (n10 === 1 && (n === 1 || n > 20)) {
11 | return f
12 | } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) {
13 | return s
14 | } else {
15 | return t
16 | }
17 | }
18 |
19 | const strings: L10nsStrings = {
20 | prefixAgo: null,
21 | prefixFromNow: 'через',
22 | suffixAgo: 'тому',
23 | suffixFromNow: null,
24 | seconds: 'менше хвилини',
25 | minute: 'хвилина',
26 | minutes: function (value) {
27 | return numpf(value, '%d хвилина', '%d хвилини', '%d хвилин')
28 | },
29 | hour: 'година',
30 | hours: function (value) {
31 | return numpf(value, '%d година', '%d години', '%d годин')
32 | },
33 | day: 'день',
34 | days: function (value) {
35 | return numpf(value, '%d день', '%d дні', '%d днів')
36 | },
37 | month: 'місяць',
38 | months: function (value) {
39 | return numpf(value, '%d місяць', '%d місяці', '%d місяців')
40 | },
41 | year: 'рік',
42 | years: function (value) {
43 | return numpf(value, '%d рік', '%d роки', '%d років')
44 | },
45 | }
46 |
47 | export default strings
48 |
--------------------------------------------------------------------------------
/src/language-strings/rs.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Serbian
5 | function numpf(n: number, f: string, s: string, t: string): string {
6 | const n10 = n % 10
7 | if (n10 === 1 && (n === 1 || n > 20)) {
8 | return f
9 | } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) {
10 | return s
11 | } else {
12 | return t
13 | }
14 | }
15 |
16 | const strings: L10nsStrings = {
17 | prefixAgo: 'pre',
18 | prefixFromNow: 'za',
19 | suffixAgo: null,
20 | suffixFromNow: null,
21 | second: 'sekund',
22 | seconds: function (value) {
23 | return numpf(value, '%d sekund', '%d sekunde', '%d sekundi')
24 | },
25 | minute: 'oko minut',
26 | minutes: function (value) {
27 | return numpf(value, '%d minut', '%d minuta', '%d minuta')
28 | },
29 | hour: 'oko jedan sat',
30 | hours: function (value) {
31 | return numpf(value, '%d sat', '%d sata', '%d sati')
32 | },
33 | day: 'jedan dan',
34 | days: function (value) {
35 | return numpf(value, '%d dan', '%d dana', '%d dana')
36 | },
37 | month: 'mesec dana',
38 | months: function (value) {
39 | return numpf(value, '%d mesec', '%d meseca', '%d meseci')
40 | },
41 | year: 'godinu dana',
42 | years: function (value) {
43 | return numpf(value, '%d godinu', '%d godine', '%d godina')
44 | },
45 | wordSeparator: ' ',
46 | }
47 |
48 | export default strings
49 |
--------------------------------------------------------------------------------
/src/language-strings/sr.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Serbian
5 | function numpf(n: number, f: string, s: string, t: string): string {
6 | const n10 = n % 10
7 | if (n10 === 1 && (n === 1 || n > 20)) {
8 | return f
9 | } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) {
10 | return s
11 | } else {
12 | return t
13 | }
14 | }
15 |
16 | const strings: L10nsStrings = {
17 | prefixAgo: 'пре',
18 | prefixFromNow: 'за',
19 | suffixAgo: null,
20 | suffixFromNow: null,
21 | second: 'секунд',
22 | seconds: function (value) {
23 | return numpf(value, '%d секунд', '%d секунде', '%d секунди')
24 | },
25 | minute: 'један минут',
26 | minutes: function (value) {
27 | return numpf(value, '%d минут', '%d минута', '%d минута')
28 | },
29 | hour: 'један сат',
30 | hours: function (value) {
31 | return numpf(value, '%d сат', '%d сата', '%d сати')
32 | },
33 | day: 'један дан',
34 | days: function (value) {
35 | return numpf(value, '%d дан', '%d дана', '%d дана')
36 | },
37 | month: 'месец дана',
38 | months: function (value) {
39 | return numpf(value, '%d месец', '%d месеца', '%d месеци')
40 | },
41 | year: 'годину дана',
42 | years: function (value) {
43 | return numpf(value, '%d годину', '%d године', '%d година')
44 | },
45 | wordSeparator: ' ',
46 | }
47 |
48 | export default strings
49 |
--------------------------------------------------------------------------------
/src/language-strings/bs.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Bosnian
5 | function numpf(n: number, f: string, s: string, t: string): string {
6 | const n10 = n % 10
7 | if (n10 === 1 && (n === 1 || n > 20)) {
8 | return f
9 | } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) {
10 | return s
11 | } else {
12 | return t
13 | }
14 | }
15 |
16 | const strings: L10nsStrings = {
17 | prefixAgo: 'prije',
18 | prefixFromNow: 'za',
19 | suffixAgo: null,
20 | suffixFromNow: null,
21 | second: 'sekund',
22 | seconds: function (value) {
23 | return numpf(value, '%d sekund', '%d sekunde', '%d sekundi')
24 | },
25 | minute: 'oko minut',
26 | minutes: function (value) {
27 | return numpf(value, '%d minut', '%d minute', '%d minuta')
28 | },
29 | hour: 'oko sat',
30 | hours: function (value) {
31 | return numpf(value, '%d sat', '%d sata', '%d sati')
32 | },
33 | day: 'oko jednog dana',
34 | days: function (value) {
35 | return numpf(value, '%d dan', '%d dana', '%d dana')
36 | },
37 | month: 'mjesec dana',
38 | months: function (value) {
39 | return numpf(value, '%d mjesec', '%d mjeseca', '%d mjeseci')
40 | },
41 | year: 'prije godinu dana ',
42 | years: function (value) {
43 | return numpf(value, '%d godinu', '%d godine', '%d godina')
44 | },
45 | wordSeparator: ' ',
46 | }
47 |
48 | export default strings
49 |
--------------------------------------------------------------------------------
/src/language-strings/hr.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Croatian
5 | function numpf(n: number, f: string, s: string, t: string): string {
6 | const n10 = n % 10
7 | if (n10 === 1 && (n === 1 || n > 20)) {
8 | return f
9 | } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) {
10 | return s
11 | } else {
12 | return t
13 | }
14 | }
15 |
16 | const strings: L10nsStrings = {
17 | prefixAgo: 'prije',
18 | prefixFromNow: 'za',
19 | suffixAgo: null,
20 | suffixFromNow: null,
21 | second: 'sekundu',
22 | seconds: function (value) {
23 | return numpf(value, '%d sekundu', '%d sekunde', '%d sekundi')
24 | },
25 | minute: 'oko minutu',
26 | minutes: function (value) {
27 | return numpf(value, '%d minutu', '%d minute', '%d minuta')
28 | },
29 | hour: 'oko jedan sat',
30 | hours: function (value) {
31 | return numpf(value, '%d sat', '%d sata', '%d sati')
32 | },
33 | day: 'jedan dan',
34 | days: function (value) {
35 | return numpf(value, '%d dan', '%d dana', '%d dana')
36 | },
37 | month: 'mjesec dana',
38 | months: function (value) {
39 | return numpf(value, '%d mjesec', '%d mjeseca', '%d mjeseci')
40 | },
41 | year: 'prije godinu dana',
42 | years: function (value) {
43 | return numpf(value, '%d godinu', '%d godine', '%d godina')
44 | },
45 | wordSeparator: ' ',
46 | }
47 |
48 | export default strings
49 |
--------------------------------------------------------------------------------
/src/language-strings/sl.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Slovenian with support for dual
5 | function numpf(n: number, a: Array): string {
6 | return a[
7 | n % 100 === 1
8 | ? 1
9 | : n % 100 === 2
10 | ? 2
11 | : n % 100 === 3 || n % 100 === 4
12 | ? 3
13 | : 0
14 | ]
15 | }
16 |
17 | const strings: L10nsStrings = {
18 | prefixAgo: null,
19 | prefixFromNow: 'čez',
20 | suffixAgo: 'nazaj',
21 | suffixFromNow: null,
22 | second: 'sekundo',
23 | seconds: function (value) {
24 | return numpf(value, ['%d sekund', '%d sekundo', '%d sekundi', '%d sekunde'])
25 | },
26 | minute: 'minuto',
27 | minutes: function (value) {
28 | return numpf(value, ['%d minut', '%d minuto', '%d minuti', '%d minute'])
29 | },
30 | hour: 'eno uro',
31 | hours: function (value) {
32 | return numpf(value, ['%d ur', '%d uro', '%d uri', '%d ure'])
33 | },
34 | day: 'en dan',
35 | days: function (value) {
36 | return numpf(value, ['%d dni', '%d dan', '%d dneva', '%d dni'])
37 | },
38 | month: 'en mesec',
39 | months: function (value) {
40 | return numpf(value, ['%d mesecev', '%d mesec', '%d meseca', '%d mesece'])
41 | },
42 | year: 'eno leto',
43 | years: function (value) {
44 | return numpf(value, ['%d let', '%d leto', '%d leti', '%d leta'])
45 | },
46 | wordSeparator: ' ',
47 | }
48 |
49 | export default strings
50 |
--------------------------------------------------------------------------------
/src/language-strings/fa.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | const hasIntl = typeof Intl !== 'undefined' && Intl.NumberFormat != null
5 |
6 | // Persian
7 | // Use DIR attribute for RTL text in Persian Language for ABBR tag .
8 | // By MB.seifollahi@gmail.com
9 | const strings: L10nsStrings = {
10 | prefixAgo: null,
11 | prefixFromNow: null,
12 | suffixAgo: 'پیش',
13 | suffixFromNow: 'از حال',
14 | seconds: 'کمتر از یک دقیقه',
15 | minute: 'حدود یک دقیقه',
16 | minutes: (value) => {
17 | if (hasIntl) {
18 | return new Intl.NumberFormat('fa').format(value) + ' دقیقه'
19 | }
20 | return '%d دقیقه'
21 | },
22 | hour: 'حدود یک ساعت',
23 | hours: (value) => {
24 | if (hasIntl) {
25 | return 'حدود %d ساعت'.replace(
26 | '%d',
27 | new Intl.NumberFormat('fa').format(value),
28 | )
29 | }
30 | return 'حدود %d ساعت'
31 | },
32 | day: 'یک روز',
33 | days: (value) => {
34 | if (hasIntl) {
35 | return '%d روز'.replace('%d', new Intl.NumberFormat('fa').format(value))
36 | }
37 | return '%d روز'
38 | },
39 | month: 'حدود یک ماه',
40 | months: (value) => {
41 | if (hasIntl) {
42 | return '%d ماه'.replace('%d', new Intl.NumberFormat('fa').format(value))
43 | }
44 | return '%d ماه'
45 | },
46 | year: 'حدود یک سال',
47 | years: (value) => {
48 | if (hasIntl) {
49 | return '%d سال'.replace('%d', new Intl.NumberFormat('fa').format(value))
50 | }
51 | return '%d سال'
52 | },
53 | wordSeparator: ' ',
54 | }
55 |
56 | export default strings
57 |
--------------------------------------------------------------------------------
/src/language-strings/ru.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Russian
5 | function numpf(n: number, f: string, s: string, t: string): string {
6 | // f - 1, 21, 31, ...
7 | // s - 2-4, 22-24, 32-34 ...
8 | // t - 5-20, 25-30, ...
9 | const n10 = n % 10
10 | if (n10 === 1 && (n === 1 || n > 20)) {
11 | return f
12 | } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) {
13 | return s
14 | } else {
15 | return t
16 | }
17 | }
18 |
19 | const strings: L10nsStrings = {
20 | prefixAgo: null,
21 | prefixFromNow: 'через',
22 | suffixAgo: function suffixAgo(value) {
23 | if (value === 0) {
24 | return ''
25 | }
26 | return 'назад'
27 | },
28 | suffixFromNow: null,
29 | seconds: function seconds(value) {
30 | if (value === 0) {
31 | return 'только что'
32 | }
33 | return numpf(value, '%d секунду', '%d секунды', '%d секунд')
34 | },
35 | minute: 'минуту',
36 | minutes: function (value) {
37 | return numpf(value, '%d минуту', '%d минуты', '%d минут')
38 | },
39 | hour: 'час',
40 | hours: function (value) {
41 | return numpf(value, '%d час', '%d часа', '%d часов')
42 | },
43 | day: 'день',
44 | days: function (value) {
45 | return numpf(value, '%d день', '%d дня', '%d дней')
46 | },
47 | month: 'месяц',
48 | months: function (value) {
49 | return numpf(value, '%d месяц', '%d месяца', '%d месяцев')
50 | },
51 | year: 'год',
52 | years: function (value) {
53 | return numpf(value, '%d год', '%d года', '%d лет')
54 | },
55 | }
56 |
57 | export default strings
58 |
--------------------------------------------------------------------------------
/src/language-strings/cs.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | // Czech
5 | function f(n: number, d: number, a: $ReadOnlyArray) {
6 | return a[d >= 0 ? 0 : a.length === 2 || n < 5 ? 1 : 2]
7 | }
8 |
9 | const strings: L10nsStrings = {
10 | prefixAgo: 'před',
11 | prefixFromNow: 'za',
12 | suffixAgo: null,
13 | suffixFromNow: null,
14 | seconds: function (n: number, d: number) {
15 | return f(n, d, ['méně než minutou', 'méně než minutu'])
16 | },
17 | minute: function (n: number, d: number) {
18 | return f(n, d, ['minutou', 'minutu'])
19 | },
20 | minutes: function (n: number, d: number) {
21 | return f(n, d, ['%d minutami', '%d minuty', '%d minut'])
22 | },
23 | hour: function (n: number, d: number) {
24 | return f(n, d, ['hodinou', 'hodinu'])
25 | },
26 | hours: function (n: number, d: number) {
27 | return f(n, d, ['%d hodinami', '%d hodiny', '%d hodin'])
28 | },
29 | day: function (n: number, d: number) {
30 | return f(n, d, ['%d dnem', '%d den'])
31 | },
32 | days: function (n: number, d: number) {
33 | return f(n, d, ['%d dny', '%d dny', '%d dní'])
34 | },
35 | month: function (n: number, d: number) {
36 | return f(n, d, ['%d měsícem', '%d měsíc'])
37 | },
38 | months: function (n: number, d: number) {
39 | return f(n, d, ['%d měsíci', '%d měsíce', '%d měsíců'])
40 | },
41 | year: function (n: number, d: number) {
42 | return f(n, d, ['%d rokem', '%d rok'])
43 | },
44 | years: function (n: number, d: number) {
45 | return f(n, d, ['%d lety', '%d roky', '%d let'])
46 | },
47 | }
48 |
49 | export default strings
50 |
--------------------------------------------------------------------------------
/examples/simple/index.js:
--------------------------------------------------------------------------------
1 | /* @flow strict */
2 | import * as React from 'react'
3 | import { useMemo } from 'react'
4 | import ReactDom from 'react-dom/client'
5 | import TimeAgo from '../../src/index'
6 | import { makeIntlFormatter } from '../../src/defaultFormatter'
7 |
8 | const appElement = document.getElementById('app')
9 |
10 | function Bare({ date }: { date: number }) {
11 | return (
12 | <>
13 | You opened this page
14 | >
15 | )
16 | }
17 |
18 | const intlFormatter = makeIntlFormatter({
19 | locale: 'en',
20 | style: 'long',
21 | numeric: 'auto',
22 | })
23 |
24 | function Intl({ date }: { date: number }) {
25 | const [locale, setLocale] = React.useState('en')
26 | const [style, setStyle] = React.useState('long')
27 | const [numeric, setNumeric] = React.useState('auto')
28 |
29 | const intlFormatter = useMemo(
30 | () =>
31 | makeIntlFormatter({
32 | locale,
33 | style,
34 | numeric,
35 | }),
36 | [locale, style, numeric],
37 | )
38 | return (
39 |
40 |
50 |
55 |
59 |
60 |
61 | )
62 | }
63 |
64 | function App() {
65 | const [date, setDate] = React.useState(Date.now())
66 |
67 | return (
68 | <>
69 | Bare
70 |
71 | Intl
72 | You opened this page
73 |
74 |
75 | >
76 | )
77 | }
78 |
79 | appElement && ReactDom.createRoot(appElement).render()
80 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-deploy.yml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | tags: [ 'v[0-9]+.[0-9]+.[0-9]+' ]
7 | pull_request:
8 | branches: [ master ]
9 |
10 | jobs:
11 | pr-test:
12 | # Only run on pull requests
13 | if: github.event_name == 'pull_request'
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v3
18 |
19 | - name: Setup Node.js
20 | uses: actions/setup-node@v3
21 | with:
22 | node-version: 22
23 |
24 | - name: Install pnpm
25 | uses: pnpm/action-setup@v2
26 | with:
27 | version: latest
28 |
29 | - name: Install dependencies
30 | run: pnpm install
31 |
32 | - name: Run tests
33 | run: pnpm test
34 |
35 | build:
36 | # Only run on pushes to master or tags
37 | if: github.event_name == 'push'
38 | runs-on: ubuntu-latest
39 |
40 | steps:
41 | - uses: actions/checkout@v3
42 |
43 | - name: Setup Node.js
44 | uses: actions/setup-node@v3
45 | with:
46 | node-version: 22
47 |
48 | - name: Install pnpm
49 | uses: pnpm/action-setup@v2
50 | with:
51 | version: latest
52 |
53 | - name: Install dependencies
54 | run: pnpm install
55 |
56 | - name: Run tests
57 | run: pnpm test
58 |
59 | deploy:
60 | needs: build
61 | # Only run on master branch and not on pull requests
62 | if: github.ref == 'refs/heads/master' && github.event_name == 'push'
63 | runs-on: ubuntu-latest
64 |
65 | steps:
66 | - uses: actions/checkout@v3
67 |
68 | - name: Setup Node.js
69 | uses: actions/setup-node@v3
70 | with:
71 | node-version: 22
72 | registry-url: 'https://registry.npmjs.org'
73 |
74 | - name: Install pnpm
75 | uses: pnpm/action-setup@v2
76 | with:
77 | version: latest
78 |
79 | - name: Install dependencies
80 | run: pnpm install
81 |
82 | - name: Run tests
83 | run: pnpm test
84 |
85 | - name: Build
86 | run: pnpm run build
87 |
88 | - name: Check if version exists
89 | id: version-check
90 | run: |
91 | PKG_VERSION=$(node -p "require('./package.json').version")
92 | PKG_NAME=$(node -p "require('./package.json').name")
93 | if npm view "$PKG_NAME@$PKG_VERSION" version &> /dev/null; then
94 | echo "Version $PKG_VERSION already exists. Skipping publish."
95 | echo "exists=true" >> $GITHUB_OUTPUT
96 | else
97 | echo "Version $PKG_VERSION does not exist. Proceeding with publish."
98 | echo "exists=false" >> $GITHUB_OUTPUT
99 | fi
100 |
101 | - name: Publish to npm
102 | if: steps.version-check.outputs.exists == 'false'
103 | run: pnpm publish
104 | env:
105 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
--------------------------------------------------------------------------------
/src/language-strings/ar.js:
--------------------------------------------------------------------------------
1 | // @flow strict
2 | import type { L10nsStrings } from '../formatters/buildFormatter'
3 |
4 | function numpf(n: number, a: Array): string {
5 | return a[
6 | n === 0
7 | ? 0
8 | : n === 1
9 | ? 1
10 | : n === 2
11 | ? 2
12 | : n % 100 >= 3 && n % 100 <= 10
13 | ? 3
14 | : n % 100 >= 11
15 | ? 4
16 | : 5
17 | ]
18 | }
19 |
20 | const strings: L10nsStrings = {
21 | prefixAgo: 'منذ',
22 | prefixFromNow: 'بعد',
23 | suffixAgo: null,
24 | suffixFromNow: null, // null OR "من الآن"
25 | second: function (value) {
26 | return numpf(value, [
27 | 'أقل من ثانية',
28 | 'ثانية واحدة',
29 | 'ثانيتين',
30 | '%d ثوانٍ',
31 | '%d ثانية',
32 | '%d ثانية',
33 | ])
34 | },
35 | seconds: function (value) {
36 | return numpf(value, [
37 | 'أقل من ثانية',
38 | 'ثانية واحدة',
39 | 'ثانيتين',
40 | '%d ثوانٍ',
41 | '%d ثانية',
42 | '%d ثانية',
43 | ])
44 | },
45 | minute: function (value) {
46 | return numpf(value, [
47 | 'أقل من دقيقة',
48 | 'دقيقة واحدة',
49 | 'دقيقتين',
50 | '%d دقائق',
51 | '%d دقيقة',
52 | 'دقيقة',
53 | ])
54 | },
55 | minutes: function (value) {
56 | return numpf(value, [
57 | 'أقل من دقيقة',
58 | 'دقيقة واحدة',
59 | 'دقيقتين',
60 | '%d دقائق',
61 | '%d دقيقة',
62 | 'دقيقة',
63 | ])
64 | },
65 | hour: function (value) {
66 | return numpf(value, [
67 | 'أقل من ساعة',
68 | 'ساعة واحدة',
69 | 'ساعتين',
70 | '%d ساعات',
71 | '%d ساعة',
72 | '%d ساعة',
73 | ])
74 | },
75 | hours: function (value) {
76 | return numpf(value, [
77 | 'أقل من ساعة',
78 | 'ساعة واحدة',
79 | 'ساعتين',
80 | '%d ساعات',
81 | '%d ساعة',
82 | '%d ساعة',
83 | ])
84 | },
85 | day: function (value) {
86 | return numpf(value, [
87 | 'أقل من يوم',
88 | 'يوم واحد',
89 | 'يومين',
90 | '%d أيام',
91 | '%d يومًا',
92 | '%d يوم',
93 | ])
94 | },
95 | days: function (value) {
96 | return numpf(value, [
97 | 'أقل من يوم',
98 | 'يوم واحد',
99 | 'يومين',
100 | '%d أيام',
101 | '%d يومًا',
102 | '%d يوم',
103 | ])
104 | },
105 | month: function (value) {
106 | return numpf(value, [
107 | 'أقل من شهر',
108 | 'شهر واحد',
109 | 'شهرين',
110 | '%d أشهر',
111 | '%d شهرًا',
112 | '%d شهر',
113 | ])
114 | },
115 | months: function (value) {
116 | return numpf(value, [
117 | 'أقل من شهر',
118 | 'شهر واحد',
119 | 'شهرين',
120 | '%d أشهر',
121 | '%d شهرًا',
122 | '%d شهر',
123 | ])
124 | },
125 | year: function (value) {
126 | return numpf(value, [
127 | 'أقل من عام',
128 | 'عام واحد',
129 | '%d عامين',
130 | '%d أعوام',
131 | '%d عامًا',
132 | ])
133 | },
134 | years: function (value) {
135 | return numpf(value, [
136 | 'أقل من عام',
137 | 'عام واحد',
138 | 'عامين',
139 | '%d أعوام',
140 | '%d عامًا',
141 | '%d عام',
142 | ])
143 | },
144 | }
145 |
146 | export default strings
147 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | #### v8.3.0
4 | - Exported Formatter, Unit, and Suffix types which were present in v7
5 |
6 | #### v8.1.0
7 | - Fixed bug where `nextFormatter` no longer worked without passing arguments
8 | - Calling `nextFormatter()` without arguments should work once again.
9 |
10 | #### v8.0.0
11 | - Code overhaul
12 | - Moved from `npm` to `pnpm`
13 | - Support React 19.x
14 | - Minor fixes
15 |
16 | #### v7.1.0
17 | - Use Intl to render persian number for farsi languages
18 | - Looking for help testing the results and then doing the same for Arabic
19 |
20 | #### v7.0.0
21 | - Added support for React 18
22 | - Added hi.js language strings
23 | - Added so.js language strings
24 | - Added oc.js language strings
25 | - Update ru.js language strings
26 | - Fix memory leak
27 | - Update dependencies
28 |
29 | #### v6.1.0
30 | - Added `eslint-plugin-react-hooks` and fixed the dependencies for hooks used in the library.
31 | Should result in a more consistent behaviour when changing props at runtime.
32 | - Updated dependencies
33 |
34 | #### v6.0.0
35 | - Added `module` field to the package.json file so you can now import ES6 modules with only
36 | a minimal amount of pre-processing applied. Relevant when targeting newer browsers only.
37 | - A bunch of internal changes:
38 | - Using `rollup` instead of `browserify` to compile the example
39 | - Updating a bunch of dependencies
40 | - Added support for React 17
41 |
42 | #### v5.2.1
43 |
44 | - Minor documentation fixes
45 |
46 | #### v4.4.0
47 | - Bug Fix: clearTimeout when setting a new timeout. Prevents unnecessary renders.
48 |
49 | #### v4.2.0
50 | - Pass in the `now` function as the last argument to the formatters.
51 | - Fix a bug in `buildFormatter` that would ignore the user-specified `now` function and just used `Date.now`
52 |
53 | #### v4.2.0
54 | - Fixed the type of `Formatter`.
55 | - It's last argument is now correctly typed to be `() => React.Node`
56 | - This last argument is now a documented feature and is going to be set to the value of of the default formatter.
57 | - Please Note, that you should not use this argument and instead import defaultFormatter from the package directly and use it as a fallback.
58 |
59 | #### v4.0.0 - v4.1.9:
60 | - Requires React ^16.
61 | - Flow types updated to the latest version (0.69)
62 | - Various bug-fixes.
63 |
64 | #### v3.x.x:
65 |
66 | - `minPeriod` and `maxPeriod` now accept seconds not milliseconds. This matches the documentation.
67 | - react-timeago now uses ES6 modules. So if you don't use ES6, your code will go from :
68 |
69 | ```js
70 | var TimeAgo = require('react-timeago')
71 | ```
72 | to:
73 | ```js
74 | var TimeAgo = require('react-timeago').default
75 | ```
76 | ES6 imports will continue to work the same way.
77 | ```js
78 | import TimeAgo from 'react-timeago'
79 | ```
80 |
81 | #### v2.2.1
82 | * Fixed the many typos introduced by me in 2.2.0. Thanks to insin for the quick PR.
83 |
84 | #### v2.2.0
85 | * FEATURE: New Props: `minPeriod` and `maxPeriod` to customize how often the Component updates.
86 |
87 | #### v2.1.1
88 |
89 | * BUG-FIX: Fixed an issue, where changing the date wouldn't correctly update the update timer.
90 |
91 | #### v2.1.0
92 |
93 | * FEATURE: Added PropType validation. It will now print a warning if you fail to pass in a date, instead of failing silently.
94 | * BUG-FIX: Pending Timeouts are cleared when the Component is unmounted
95 | * BUG-FIX: When new Props are passed in, the component will update itself correctly. Now you can flip the live switch on and off.
96 | * FEATURE: The formatter function gets the original date as the fourth argument, for more custom date formats.
97 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-timeago",
3 | "version": "8.3.0",
4 | "description": "A simple Time-Ago component for ReactJs",
5 | "main": "lib/index.js",
6 | "types": "es6/index.d.ts",
7 | "exports": {
8 | ".": {
9 | "import": "./es6/index.js",
10 | "require": "./lib/index.js"
11 | },
12 | "./dateParser": {
13 | "import": "./es6/dateParser.js",
14 | "require": "./lib/dateParser.js"
15 | },
16 | "./defaultFormatter": {
17 | "import": "./es6/defaultFormatter.js",
18 | "require": "./lib/defaultFormatter.js"
19 | },
20 | "./language-strings/*": {
21 | "import": "./es6/language-strings/*.js",
22 | "require": "./lib/language-strings/*.js"
23 | },
24 | "./formatters/buildFormatter": {
25 | "import": "./es6/formatters/buildFormatter.js",
26 | "require": "./lib/formatters/buildFormatter.js"
27 | },
28 | "./lib/language-strings/*": {
29 | "import": "./es6/language-strings/*.js",
30 | "require": "./lib/language-strings/*.js"
31 | },
32 | "./lib/formatters/buildFormatter": {
33 | "import": "./es6/formatters/buildFormatter.js",
34 | "require": "./lib/formatters/buildFormatter.js"
35 | }
36 | },
37 | "module": "es6/index.js",
38 | "scripts": {
39 | "babel-es6": "ES6=true babel src/ --out-dir es6/",
40 | "postbabel-es6": "node ./scripts/gen-types.js -i src -o es6/",
41 | "babel": "babel src/ --out-dir lib/",
42 | "postbabel": "node ./scripts/gen-types.js -i src -o lib/",
43 | "build": "npm run babel && npm run babel-es6 && npm run example",
44 | "example": "rollup -c ./rollup.config.mjs",
45 | "prepublish": "npm run build",
46 | "test": "jest --coverage"
47 | },
48 | "jest": {
49 | "testEnvironment": "jsdom"
50 | },
51 | "repository": {
52 | "type": "git",
53 | "url": "https://github.com/naman34/react-timeago.git"
54 | },
55 | "keywords": [
56 | "React",
57 | "ReactJS",
58 | "Time",
59 | "Ago",
60 | "TimeAgo",
61 | "ender"
62 | ],
63 | "author": "Naman Goel",
64 | "license": "MIT",
65 | "bugs": {
66 | "url": "https://github.com/nmn/react-timeago/issues"
67 | },
68 | "homepage": "https://github.com/nmn/react-timeago",
69 | "peerDependencies": {
70 | "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
71 | },
72 | "devDependencies": {
73 | "@babel/cli": "^7.27.0",
74 | "@babel/preset-env": "^7.18.2",
75 | "@babel/preset-flow": "^7.17.12",
76 | "@babel/preset-react": "^7.17.12",
77 | "@babel/preset-stage-1": "^7.8.3",
78 | "@rollup/plugin-babel": "^6.0.4",
79 | "@rollup/plugin-commonjs": "^28.0.2",
80 | "@rollup/plugin-node-resolve": "^16.0.1",
81 | "@rollup/plugin-replace": "^6.0.2",
82 | "@testing-library/jest-dom": "^6.6.3",
83 | "@testing-library/react": "^16.3.0",
84 | "babel-jest": "^29.7.0",
85 | "babel-plugin-syntax-hermes-parser": "^0.28.0",
86 | "eslint-config-prettier": "^10.1.2",
87 | "eslint-plugin-flowtype": "^8.0.3",
88 | "eslint-plugin-ft-flow": "^3.0.0",
89 | "eslint-plugin-import": "^2.26.0",
90 | "eslint-plugin-prettier": "^4.0.0",
91 | "eslint-plugin-react-hooks": "^5.1.0",
92 | "eslint": "^8.1.0",
93 | "flow-api-translator": "^0.28.0",
94 | "flow-bin": "^0.267.0",
95 | "hermes-eslint": "^0.28.0",
96 | "jest-environment-jsdom": "^29.7.0",
97 | "jest": "^29.7.0",
98 | "prettier-plugin-hermes-parser": "^0.28.0",
99 | "prettier": "^2.8.8",
100 | "react-dom": "^19.1.0",
101 | "react": "^19.1.0",
102 | "rollup": "^4.39.0",
103 | "yargs": "^17.7.2"
104 | },
105 | "prettier": {
106 | "plugins": [
107 | "prettier-plugin-hermes-parser"
108 | ],
109 | "printWidth": 80,
110 | "semi": false,
111 | "trailingComma": "all",
112 | "singleQuote": true
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/__tests__/index.js:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom'
2 | import { render, fireEvent, screen } from '@testing-library/react'
3 |
4 | import React from 'react'
5 |
6 | import TimeAgo from '../src'
7 | import buildFormatter from '../src/formatters/buildFormatter'
8 | import TWStrings from '../src/language-strings/zh-TW'
9 |
10 | describe('TimeAgo', () => {
11 | test('just now', () => {
12 | render()
13 | expect(screen.getByText('0 seconds ago')).toBeInTheDocument()
14 | })
15 |
16 | test('1 second ago', () => {
17 | render()
18 | expect(screen.getByText('1 second ago')).toBeInTheDocument()
19 | })
20 |
21 | test('2 seconds ago', () => {
22 | render()
23 | expect(screen.getByText('2 seconds ago')).toBeInTheDocument()
24 | })
25 |
26 | test('1 minute ago', () => {
27 | render()
28 | expect(screen.getByText('1 minute ago')).toBeInTheDocument()
29 | })
30 |
31 | test('2 minutes ago', () => {
32 | render()
33 | expect(screen.getByText('2 minutes ago')).toBeInTheDocument()
34 | })
35 |
36 | test('1 hour ago', () => {
37 | render()
38 | expect(screen.getByText('1 hour ago')).toBeInTheDocument()
39 | })
40 |
41 | test('2 hours ago', () => {
42 | render()
43 | expect(screen.getByText('2 hours ago')).toBeInTheDocument()
44 | })
45 |
46 | test('1 day ago', () => {
47 | render()
48 | expect(screen.getByText('1 day ago')).toBeInTheDocument()
49 | })
50 |
51 | test('1 week ago', () => {
52 | render()
53 | expect(screen.getByText('1 week ago')).toBeInTheDocument()
54 | })
55 |
56 | test('21 days ago', () => {
57 | const timeAgoFormatConfig = {
58 | prefixAgo: null,
59 | prefixFromNow: null,
60 | suffixAgo: 'ago',
61 | suffixFromNow: 'from now',
62 | seconds: '%d sec',
63 | minutes: '%d min',
64 | hour: '%d hr',
65 | hours: '%d hrs',
66 | day: '%d day',
67 | days: '%d days',
68 | wordSeparator: ' ',
69 | }
70 |
71 | const timeAgoFormatter = buildFormatter(timeAgoFormatConfig)
72 |
73 | render(
74 | new Date('Tue Apr 24 2018 15:12:51 GMT-0400 (EDT)').getTime()}
78 | />,
79 | )
80 | expect(screen.getByText('21 days ago')).toBeInTheDocument()
81 | })
82 |
83 | /* zh-TW */
84 | const formatter = buildFormatter(TWStrings)
85 |
86 | /* 1 week ago in zh-TW */
87 | it('1 week ago in zh-TW', () => {
88 | render(
89 | ,
93 | )
94 | expect(screen.getByText('7天之前')).toBeInTheDocument()
95 | })
96 |
97 | test('allow custom wordSeparator', () => {
98 | const strings = Object.assign({}, TWStrings, { wordSeparator: 'x' })
99 | const formatter = buildFormatter(strings)
100 | render(
101 | ,
105 | )
106 | expect(screen.getByText('7天x之前')).toBeInTheDocument()
107 | })
108 |
109 | test('on date change, now() should be updated', () => {
110 | const start = 1000000000000
111 | const second = 1_000
112 | const now = jest.fn(() => start + 10 * second) // T+10
113 |
114 | const Component = ({ date }) => <>
115 |
116 | >
117 |
118 | const { rerender } = render()
119 | expect(screen.getByText('10 seconds ago')).toBeInTheDocument()
120 |
121 | // advance time by another 10s - T+20
122 | now.mockImplementation(() => start + second * 20)
123 |
124 | // add 1s to previous value
125 | rerender()
126 |
127 | // 20 - 1 = 19, because new now() value should be applied
128 | expect(screen.getByText('19 seconds ago')).toBeInTheDocument()
129 | })
130 | })
--------------------------------------------------------------------------------
/src/formatters/buildFormatter.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import type { Node as ReactNode } from 'react'
4 | import type { Formatter, Unit, Suffix } from '../types'
5 |
6 | type StringOrFn = string | ((value: number, millisDelta: number) => string)
7 | type NumberArray = [
8 | string,
9 | string,
10 | string,
11 | string,
12 | string,
13 | string,
14 | string,
15 | string,
16 | string,
17 | string,
18 | ]
19 |
20 | export type L10nsStrings = {
21 | prefixAgo?: ?StringOrFn,
22 | prefixFromNow?: ?StringOrFn,
23 | suffixAgo?: ?StringOrFn,
24 | suffixFromNow?: ?StringOrFn,
25 | second?: ?StringOrFn,
26 | seconds?: ?StringOrFn,
27 | minute?: ?StringOrFn,
28 | minutes?: ?StringOrFn,
29 | hour?: ?StringOrFn,
30 | hours?: ?StringOrFn,
31 | day?: ?StringOrFn,
32 | days?: ?StringOrFn,
33 | week?: ?StringOrFn,
34 | weeks?: ?StringOrFn,
35 | month?: ?StringOrFn,
36 | months?: ?StringOrFn,
37 | year?: ?StringOrFn,
38 | years?: ?StringOrFn,
39 | wordSeparator?: ?string,
40 | numbers?: ?NumberArray,
41 | }
42 |
43 | // If the numbers array is present, format numbers with it,
44 | // otherwise just cast the number to a string and return it
45 | const normalizeNumber = (numbers: ?NumberArray, value: number) =>
46 | numbers && numbers.length === 10
47 | ? String(value)
48 | .split('')
49 | .map((digit: string) =>
50 | digit.match(/^[0-9]$/)
51 | ? ((numbers: any): NumberArray)[parseInt(digit)]
52 | : digit,
53 | )
54 | .join('')
55 | : String(value)
56 |
57 | // Take a string or a function that takes number of days and returns a string
58 | // and provide a uniform API to create string parts
59 | const normalizeFn =
60 | (value: number, distanceMillis: number, numbers?: NumberArray) =>
61 | (stringOrFn: StringOrFn) =>
62 | typeof stringOrFn === 'function'
63 | ? stringOrFn(value, distanceMillis).replace(
64 | /%d/g,
65 | normalizeNumber(numbers, value),
66 | )
67 | : stringOrFn.replace(/%d/g, normalizeNumber(numbers, value))
68 |
69 | const pluralize = (unit: Unit) => {
70 | switch (unit) {
71 | case 'second':
72 | return 'seconds'
73 | case 'minute':
74 | return 'minutes'
75 | case 'hour':
76 | return 'hours'
77 | case 'day':
78 | return 'days'
79 | case 'week':
80 | return 'weeks'
81 | case 'month':
82 | return 'months'
83 | case 'year':
84 | return 'years'
85 | default:
86 | return unit
87 | }
88 | }
89 |
90 | export default function buildFormatter(strings: L10nsStrings): Formatter {
91 | return function formatter(
92 | _value: number,
93 | _unit: Unit,
94 | suffix: Suffix,
95 | epochMilliseconds: number,
96 | _nextFormmater: Formatter,
97 | now: () => number,
98 | ) {
99 | const current = now()
100 | let value = _value
101 | let unit = _unit
102 | // convert weeks to days if strings don't handle weeks
103 | if (unit === 'week' && !strings.week && !strings.weeks) {
104 | const days = Math.round(
105 | Math.abs(epochMilliseconds - current) / (1000 * 60 * 60 * 24),
106 | )
107 | value = days
108 | unit = 'day'
109 | }
110 |
111 | // create a normalize function for given value
112 | const normalize = normalizeFn(
113 | value,
114 | current - epochMilliseconds,
115 | strings.numbers != null ? strings.numbers : undefined,
116 | )
117 |
118 | // The eventual return value stored in an array so that the wordSeparator can be used
119 | const dateString: Array = []
120 |
121 | // handle prefixes
122 | if (suffix === 'ago' && strings.prefixAgo) {
123 | dateString.push(normalize(strings.prefixAgo))
124 | }
125 | if (suffix === 'from now' && strings.prefixFromNow) {
126 | dateString.push(normalize(strings.prefixFromNow))
127 | }
128 |
129 | // Handle Main number and unit
130 | const isPlural = value > 1
131 | if (isPlural) {
132 | const stringFn: StringOrFn =
133 | strings[pluralize(unit)] || strings[unit] || '%d ' + unit
134 | dateString.push(normalize(stringFn))
135 | } else {
136 | const stringFn: StringOrFn =
137 | strings[unit] || strings[pluralize(unit)] || '%d ' + unit
138 | dateString.push(normalize(stringFn))
139 | }
140 |
141 | // Handle Suffixes
142 | if (suffix === 'ago' && strings.suffixAgo) {
143 | dateString.push(normalize(strings.suffixAgo))
144 | }
145 | if (suffix === 'from now' && strings.suffixFromNow) {
146 | dateString.push(normalize(strings.suffixFromNow))
147 | }
148 |
149 | // join the array into a string and return it
150 | const wordSeparator =
151 | typeof strings.wordSeparator === 'string' ? strings.wordSeparator : ' '
152 | return dateString.join(wordSeparator)
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/scripts/gen-types.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const fsPromises = require('fs/promises')
4 | const monorepoPackage = require('../package.json')
5 | const path = require('path')
6 | const translate = require('flow-api-translator')
7 | const yargs = require('yargs/yargs')
8 |
9 | const prettierConfig = {
10 | ...monorepoPackage.prettier,
11 | // semi: true,
12 | }
13 |
14 | async function generateTypes(inputDir, outputDir, rootDir) {
15 | const rootPath = rootDir ?? path.resolve(inputDir, '../')
16 |
17 | await fsPromises.mkdir(outputDir, { recursive: true })
18 | let dirents = await fsPromises.readdir(inputDir, { withFileTypes: true })
19 |
20 | const jsFlowFiles = dirents
21 | .filter((dirent) => dirent.name.endsWith('.js.flow'))
22 | .map((dirent) => dirent.name.replace(/\.js\.flow$/, '.js'))
23 |
24 | const dTsFiles = dirents
25 | .filter((dirent) => dirent.name.endsWith('.d.ts'))
26 | .map((dirent) => dirent.name)
27 |
28 | dirents = dirents.filter((dirents) => !jsFlowFiles.includes(dirents.name))
29 |
30 | for (const dirent of dirents) {
31 | try {
32 | const inputFullPath = path.join(inputDir, dirent.name)
33 | const outputFullPath = path
34 | .join(outputDir, dirent.name)
35 | .replace(/\.js\.flow$/, '.js')
36 | if (dirent.isDirectory()) {
37 | if (dirent.name !== '__tests__') {
38 | await generateTypes(inputFullPath, outputFullPath, rootPath)
39 | }
40 | } else {
41 | // // dirent is a file
42 | if (dirent.name.endsWith('.d.ts')) {
43 | const fileContents = await fsPromises.readFile(inputFullPath, 'utf8')
44 | await fsPromises.writeFile(
45 | path.join(outputDir, dirent.name),
46 | fileContents,
47 | )
48 | }
49 |
50 | if (dirent.name.endsWith('.js') || dirent.name.endsWith('.js.flow')) {
51 | try {
52 | let fileContents = await fsPromises.readFile(inputFullPath, 'utf8')
53 | fileContents = preprocessFileContents(fileContents)
54 | const outputFlowContents = await translate.translateFlowToFlowDef(
55 | fileContents,
56 | prettierConfig,
57 | )
58 |
59 | await fsPromises.writeFile(
60 | `${outputFullPath}.flow`,
61 | outputFlowContents,
62 | )
63 | const tsOutputName = dirent.name
64 | .replace(/\.js$/, '.d.ts')
65 | .replace(/\.js\.flow$/, '.d.ts')
66 | if (dTsFiles.includes(tsOutputName)) {
67 | continue
68 | }
69 | const outputTSContents = await translate.translateFlowToTSDef(
70 | fileContents,
71 | prettierConfig,
72 | )
73 | await fsPromises.writeFile(
74 | outputFullPath.replace(/\.js$/, '.d.ts'),
75 | // Typescript Prefers `NodePath` unlike `NodePath<>` in Flow
76 | // `flow-api-translator` doesn't handle this case yet.
77 | postProcessTSOutput(outputTSContents),
78 | )
79 | } catch (err) {
80 | console.log(`Failed to process file: ${inputFullPath}`)
81 | throw err
82 | }
83 | }
84 | }
85 | } catch (err) {
86 | console.error(`Failed to process file: ${dirent.name}`)
87 | console.error(err)
88 | }
89 | }
90 | }
91 |
92 | // Changes to files before they are processed by `flow-api-translator`
93 | // to patch the bugs in the translator
94 | function preprocessFileContents(inputCode) {
95 | // `flow-api-translator` doesn't handle Flow comments correctly
96 | while (inputCode.includes('/*::')) {
97 | const startIndex = inputCode.indexOf('/*::')
98 | const endIndex = inputCode.indexOf('*/', startIndex)
99 |
100 | const comment = inputCode.substring(startIndex, endIndex + 2)
101 | const replacement = comment.substring(4, comment.length - 2)
102 |
103 | inputCode = inputCode.replace(comment, replacement)
104 | }
105 | return inputCode
106 | }
107 |
108 | function postProcessTSOutput(outputCode) {
109 | const result = outputCode
110 | .replace(/<>/g, '')
111 | .replace(/\$ReadOnlyMap/g, 'ReadonlyMap')
112 | .replace(/\$ReadOnlySet/g, 'ReadonlySet')
113 |
114 | return result
115 | }
116 |
117 | const args = yargs(process.argv)
118 | .option('inputDir', {
119 | alias: 'i',
120 | type: 'string',
121 | })
122 | .option('outputDir', {
123 | alias: 'o',
124 | type: 'string',
125 | }).argv
126 |
127 | const inputDir = path.join(process.cwd(), args.inputDir)
128 | const outputDir = path.join(process.cwd(), args.outputDir)
129 | generateTypes(inputDir, outputDir)
130 | .then(() => {
131 | console.log('Done generating type definition files')
132 | })
133 | .catch((err) => {
134 | console.error(err)
135 | process.exit(1)
136 | })
137 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import * as React from 'react'
4 | import { useEffect, useState } from 'react'
5 | import dateParser from './dateParser'
6 | import defaultFormatter from './defaultFormatter'
7 | export type { Formatter, Suffix, Unit } from './types'
8 |
9 | export type Props = $ReadOnly<{
10 | /** If the component should update itself over time */
11 | live?: boolean,
12 | /** minimum amount of time in seconds between re-renders */
13 | minPeriod?: number,
14 | /** Maximum time between re-renders in seconds. The component should update at least once every `x` seconds */
15 | maxPeriod?: number,
16 | /** The container to render the string into. You could use a string like `span` or a custom component */
17 | component?: string | React.ComponentType<{ ... }>,
18 | /**
19 | * A title used for setting the title attribute if a