-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(@formatjs/icu-messageformat-parser): preprocess 'j' date time sk…
…eleton before parsing as suggested in the Unicode guide (#3544) feat(intl-messageformat): add support for 'j' time skeleton format Co-authored-by: Batjaa Batbold <batbob@amazon.comm>
- Loading branch information
Showing
11 changed files
with
1,998 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96 changes: 96 additions & 0 deletions
96
packages/icu-messageformat-parser/date-time-pattern-generator.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import {timeData} from './time-data.generated' | ||
|
||
/** | ||
* Returns the best matching date time pattern if a date time skeleton | ||
* pattern is provided with a locale. Follows the Unicode specification: | ||
* https://www.unicode.org/reports/tr35/tr35-dates.html#table-mapping-requested-time-skeletons-to-patterns | ||
* @param skeleton date time skeleton pattern that possibly includes j, J or C | ||
* @param locale | ||
*/ | ||
export function getBestPattern(skeleton: string, locale: Intl.Locale) { | ||
let skeletonCopy = '' | ||
for (let patternPos = 0; patternPos < skeleton.length; patternPos++) { | ||
const patternChar = skeleton.charAt(patternPos) | ||
|
||
if (patternChar === 'j') { | ||
let extraLength = 0 | ||
while ( | ||
patternPos + 1 < skeleton.length && | ||
skeleton.charAt(patternPos + 1) === patternChar | ||
) { | ||
extraLength++ | ||
patternPos++ | ||
} | ||
|
||
let hourLen = 1 + (extraLength & 1) | ||
let dayPeriodLen = extraLength < 2 ? 1 : 3 + (extraLength >> 1) | ||
let dayPeriodChar = 'a' | ||
let hourChar = getDefaultHourSymbolFromLocale(locale) | ||
|
||
if (hourChar == 'H' || hourChar == 'k') { | ||
dayPeriodLen = 0 | ||
} | ||
|
||
while (dayPeriodLen-- > 0) { | ||
skeletonCopy += dayPeriodChar | ||
} | ||
while (hourLen-- > 0) { | ||
skeletonCopy = hourChar + skeletonCopy | ||
} | ||
} else if (patternChar === 'J') { | ||
skeletonCopy += 'H' | ||
} else { | ||
skeletonCopy += patternChar | ||
} | ||
} | ||
|
||
return skeletonCopy | ||
} | ||
|
||
/** | ||
* Maps the [hour cycle type](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/hourCycle) | ||
* of the given `locale` to the corresponding time pattern. | ||
* @param locale | ||
*/ | ||
function getDefaultHourSymbolFromLocale(locale: Intl.Locale): string { | ||
let hourCycle = locale.hourCycle | ||
|
||
if ( | ||
hourCycle === undefined && | ||
// @ts-ignore hourCycle(s) is not identified yet | ||
locale.hourCycles && | ||
// @ts-ignore | ||
locale.hourCycles.length | ||
) { | ||
// @ts-ignore | ||
hourCycle = locale.hourCycles[0] | ||
} | ||
|
||
if (hourCycle) { | ||
switch (hourCycle) { | ||
case 'h24': | ||
return 'k' | ||
case 'h23': | ||
return 'H' | ||
case 'h12': | ||
return 'h' | ||
case 'h11': | ||
return 'K' | ||
default: | ||
throw new Error('Invalid hourCycle') | ||
} | ||
} | ||
|
||
// TODO: Once hourCycle is fully supported remove the following with data generation | ||
const languageTag = locale.language | ||
let regionTag: string | undefined | ||
if (languageTag !== 'root') { | ||
regionTag = locale.maximize().region | ||
} | ||
const hourCycles = | ||
timeData[regionTag || ''] || | ||
timeData[languageTag || ''] || | ||
timeData[`${languageTag}-001`] || | ||
timeData['001'] | ||
return hourCycles[0] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
packages/icu-messageformat-parser/scripts/time-data-gen.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import * as rawTimeData from 'cldr-core/supplemental/timeData.json' | ||
import {outputFileSync} from 'fs-extra' | ||
import minimist from 'minimist' | ||
|
||
function main(args: minimist.ParsedArgs) { | ||
const {timeData} = rawTimeData.supplemental | ||
const data = Object.keys(timeData).reduce( | ||
(all: Record<string, string[]>, k) => { | ||
all[k.replace('_', '-')] = | ||
timeData[k as keyof typeof timeData]._allowed.split(' ') | ||
return all | ||
}, | ||
{} | ||
) | ||
outputFileSync( | ||
args.out, | ||
`// @generated from time-data-gen.ts | ||
// prettier-ignore | ||
export const timeData: Record<string, string[]> = ${JSON.stringify( | ||
data, | ||
undefined, | ||
2 | ||
)}; | ||
` | ||
) | ||
} | ||
|
||
if (require.main === module) { | ||
main(minimist(process.argv)) | ||
} |
Oops, something went wrong.