Files
cm-app-crawlspider/node_modules/relative-time-format/bin/generate-locale-messages.js

267 lines
9.5 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import path from 'path'
import fs from 'fs-extra'
import extractRelativeTimeMessages from '../source/CLDR/extractRelativeTimeMessages'
import getLocalesListInCLDR from '../source/CLDR/getLocalesList'
// CLDR stubs missing translations with English ones.
// This can be used to find out whether a translation is missing.
const LONG_STYLE_TRANSLATION_STUB = `{
"year": {
"previous": "last year",
"current": "this year",
"next": "next year",
"past": "-{0} y",
"future": "+{0} y"
}`
// Generate plurals first, then run this script.
// Generating plurals creates locale folder structure.
//
// ```
// npm run generate-locale-quantifiers
// npm run generate-locale-messages
// // npm run generate-load-all-locales
// ````
for (const locale of getLocalesListInCLDR())
{
if (
// Different variations of "en" language have `en/short.json` and `en/narrow.json`
// which differ from one another by a simple dot, e.g. `yr.` vs `yr`.
// To reduce the resulting bundle size this dot difference is considered unimportant.
locale.indexOf('en-') === 0 ||
// For "pt" language the relative time messages seem to be different
// from all other "pt-" variations which are identical to "pt-PT".
// Seems like a bug in CLDR.
// To reduce the resulting bundle size "pt" language is discarded
// and "pt-PT" is used instead. All other "pt-" variations
// seems to be identical to "pt-PT" so they're not included.
locale.indexOf('pt-') === 0
) {
// Delete the locale folder.
// (previously created by `npm run generate-locale-quantifiers`).
fs.removeSync(path.join(__dirname, '../locale', locale))
continue
}
// console.log(locale)
// "language" is the top-most parent locale of the `locale`.
const language = locale.split('-')[0]
// For "pt" language the relative time messages seem to be different
// from all other "pt-" variations which are identical to "pt-PT".
// Seems like a bug in CLDR.
// To reduce the resulting bundle size "pt" language is discarded
// and "pt-PT" is used instead. All other "pt-" variations
// seems to be identical to "pt-PT" so they're not included.
const localeInCLDR = locale === 'pt' ? 'pt-PT' : locale
const cldrJsonPath = `cldr-dates-full/main/${localeInCLDR}/dateFields.json`
const localeDirectory = path.join(__dirname, '../locale', locale)
const languageDirectory = path.join(__dirname, '../locale', language)
// If there's no pluralization classifier function
// for this language then don't add it.
const quantifyDirectory = findQuantifyDirectory(locale)
const localeMessages = extractRelativeTimeMessages(require(cldrJsonPath))
// "long" messages are always present.
if (!localeMessages.long) {
throw new Error(`Default (long) locale data is missing for locale "${locale}".`)
}
// If there are no translations for a locale then skip it.
if (JSON.stringify(localeMessages.long, null, '\t').indexOf(LONG_STYLE_TRANSLATION_STUB) === 0) {
// console.log(`No translation for "${locale}". Skipping.`)
fs.removeSync(localeDirectory)
continue
}
// "short" messages are always present.
if (!localeMessages.short) {
throw new Error(`Short locale data is missing for locale "${locale}".`)
}
// "narrow" messages are always present.
if (!localeMessages.narrow) {
throw new Error(`Narrow locale data is missing for locale "${locale}".`)
}
// Drop duplicate quantifier messages.
compactQuantifiersData(localeMessages.long)
compactQuantifiersData(localeMessages.short)
compactQuantifiersData(localeMessages.narrow)
// What are "narrow" and "short" styles and how are they constructed:
// http://cldr.unicode.org/translation/plurals#TOC-Narrow-and-Short-Forms
// Create `index.js` file in the locale directory.
fs.outputFileSync(
path.join(localeDirectory, 'index.js'),
`
module.exports = {
${[
"locale: '" + locale + "'",
"long: require('" + createTimeLabels(locale, 'long', localeMessages) + "')",
"short: require('" + createTimeLabels(locale, 'short', localeMessages) + "')",
"narrow: require('" + createTimeLabels(locale, 'narrow', localeMessages) + "')",
quantifyDirectory && ("quantify: require('" + quantifyDirectory + "/quantify')")
]
.filter(_ => _)
.join(',\n\t')}
}
`.trim()
)
// Remove all locales containing just `index.js`
// which means they're fully inherting from their parent locale.
for (const locale of getLocalesListGenerated()) {
const files = fs.readdirSync(path.join(__dirname, '../locale', locale))
if (files.length === 1 && files[0] === 'index.js') {
fs.removeSync(path.resolve(__dirname, '../locale', locale))
}
}
// Remove strange locales.
fs.removeSync(path.resolve(__dirname, '../locale/en-001'))
fs.removeSync(path.resolve(__dirname, '../locale/en-150'))
fs.removeSync(path.resolve(__dirname, '../locale/en-US-POSIX'))
fs.removeSync(path.resolve(__dirname, '../locale/es-419'))
}
/**
* CLDR data always has relative time messages duplicated
* for all keys if they're the same.
* For example, if relative time messages are the same for
* "one", "many" and "other" they will still be duplicated
* in CLDR data files.
* This function removes such duplication to reduce the
* resulting bundle size.
* Mutates the object passed as the argument directly.
* @param {object} flavor — Relative time messages of a given flavor.
*/
function compactQuantifiersData(flavour) {
for (const unit of Object.keys(flavour)) {
for (const pastOrFuture of Object.keys(flavour[unit])) {
for (const quantifier of Object.keys(flavour[unit][pastOrFuture])) {
// "other" is the one holding the relative time message in case of duplication.
if (quantifier === 'other') {
continue
}
if (flavour[unit][pastOrFuture][quantifier] === flavour[unit][pastOrFuture].other) {
delete flavour[unit][pastOrFuture][quantifier]
}
}
}
}
}
/**
* Returns a list of all supported locales.
* @return {string[]}
*/
function getLocalesListGenerated() {
return fs.readdirSync(path.join(__dirname, '../locale'))
.filter(_ => fs.statSync(path.join(__dirname, '../locale', _)).isDirectory())
}
/**
* Creates a file with the time messages
* of a given style for a given locale.
* @param {string} locale
* @param {string} style
* @param {object} localeMessages — Time messages object containing all styles.
* @return {object} The relative path to the time messages file.
*/
function createTimeLabels(locale, style, localeMessages) {
const content = JSON.stringify(localeMessages[style], null, '\t')
// `sr-Cyrl-BA` -> `sr-Cyrl` -> `sr`.
const parentLocale = findParentLocaleHavingFile(locale, `${style}.json`, {
condition: (file) => fs.readFileSync(file, 'utf-8') === content
})
if (parentLocale) {
return `../${parentLocale}/${style}.json`
}
fs.outputFileSync(path.join(__dirname, '../locale', locale, `${style}.json`), content)
return `./${style}.json`
}
/**
* Returns the relative path to a directory where
* `quantify.js` resides for a given locale.
* For example, there are different locales: "ar" and "ar-AE".
* But their `quantify()` function is identical.
* Therefore, it's only stored inside `ar` folder
* and `getQuantifyDirectory('ar-AE')` is "../ar".
* @param {string} locale
* @return {string} [directory]
*/
function findQuantifyDirectory(locale) {
// Look in parent locales' folders.
// Example: `sr-Cyrl-BA` -> `sr-Cyrl` -> `sr`.
const parentLocale = findParentLocaleHavingFile(locale, 'quantify.js')
if (parentLocale) {
return `../${parentLocale}`
}
// Look in this locale's folder.
if (fs.existsSync(path.join(__dirname, '../locale', locale, 'quantify.js'))) {
return '.'
}
}
/**
* Returns the relative path to a directory where
* a given "flavor" (short, long, narrow) labels file
* resides for a given locale.
* @param {string} locale
* @param {string} flavor
* @return {string} [directory]
*/
function findFlavorDirectory(locale, flavour) {
// Look in parent locales' folders.
// Example: `sr-Cyrl-BA` -> `sr-Cyrl` -> `sr`.
const parentLocale = findParentLocaleHavingFile(locale, `${flavour}.json`, { self: true })
if (parentLocale) {
if (parentLocale === locale) {
return '.'
}
return `../${parentLocale}`
}
}
/**
* Some files are inherited from a parent locale to a child locale.
* For example, there are different locales: "ar" and "ar-AE".
* But their `quantify()` function is identical.
* Therefore, it's only stored inside `ar` folder
* and "ar-AE" locale reuses that file.
* @param {string} locale
* @param {string} fileName
* @param {object} [options]
* @param {boolean} [options.self] — Allow returning same `locale`.
* @param {function} [options.condition] — An extra condition imposed on the absolute file path of the file.
* @return {string} [locale]
*/
function findParentLocaleHavingFile(locale, fileName, options = {}) {
const restParts = locale.split('-')
const parts = []
let inheritFrom
while (restParts.length > 0) {
parts.push(restParts.shift())
const parentLocale = parts.join('-')
if (!options.self && parentLocale === locale) {
continue
}
if (!fs.existsSync(path.join(__dirname, '../locale', parentLocale))) {
continue
}
if (fs.existsSync(path.join(__dirname, '../locale', parentLocale, fileName))) {
if (!options.condition || options.condition(path.join(__dirname, '../locale', parentLocale, fileName))) {
inheritFrom = parentLocale
}
}
}
return inheritFrom
}