import attic from "./attic.js";
import defaultLocale from "./locales/default.js";
import enLocale from "./locales/en.js";
import nlLocale from "./locales/nl.js";
import cyLocale from "./locales/cy.js";
import eoLocale from "./locales/eo.js";
import grcLocale from "./locales/grc.js";
import plLocale from "./locales/pl.js";
import zhLocale from "./locales/zh.js";
import zhHantLocale from "./locales/zh-Hant.js";
const getNestedObject = (nestedObj, pathArr) => {
return pathArr.reduce(
(obj, key) => (obj && obj[key] !== "undefined" ? obj[key] : undefined),
nestedObj
);
};
let grimm = {
escape: text =>
text.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&"),
enumerate: (num, system) => {
if (system == "roman") {
if (num == 0) {
return "N";
}
const tensome = (i, v, x) => [
"",
`${i}`,
`${i}${i}`,
`${i}${i}${i}`,
`${i}${v}`,
`${v}`,
`${v}${i}`,
`${v}${i}${i}`,
`${v}${i}${i}${i}`,
`${i}${x}`
];
const numerals = [
tensome("I", "V", "X"),
tensome("X", "L", "C"),
tensome("C", "D", "M"),
tensome("M", "ↁ", "ↂ")
];
let output = "";
for (let i = numerals.length - 1; i >= 0; i--) {
output += numerals[i][Math.floor(num / 10 ** i) % 10];
}
return output;
}
if (system == "greek") {
if (num == 0) {
return "·ʹ";
}
const numerals = [
["", "α", "β", "γ", "δ", "ε", "ϝ", "ζ", "η", "θ"],
["", "ι", "κ", "λ", "μ", "ν", "ξ", "ο", "π", "ϟ"],
["", "ρ", "σ", "τ", "υ", "φ", "χ", "ψ", "ω", "ϡ"]
];
numerals.push(numerals[0].map(x => (x == "" ? "" : `͵${x}`)));
let output = "";
for (let i = numerals.length - 1; i >= 0; i--) {
output += numerals[i][Math.floor(num / 10 ** i) % 10];
}
return `${output}ʹ`;
}
return `${num}`;
},
slugify: (text, options) => {
text = `${text}`.normalize("NFD");
if (options?.html) {
text = text.replace(/<(.*?)>/g, "");
}
return text
.replace(/[\u0300-\u036f]/g, "")
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/(^-|-$)+/g, "");
},
sentenceCase: (locale, text) =>
text[0].toLocaleUpperCase(
grimm.dict?.[locale]?.meta?.intlFallback ?? locale
) + text.slice(1),
translate: (locale, keyword, ...args) => {
let keywordArray = keyword.split(".");
let gazettedName = undefined;
if (keyword.match(/^loc\.language\./)) {
const languageGazetteer = new Intl.DisplayNames(
[...(grimm.dict?.[locale]?.meta?.intlFallback ?? locale)],
{
type: "language"
}
);
if (
languageGazetteer.of(keyword.replace("loc.language.", "")) !=
keyword.replace("loc.language.", "")
) {
gazettedName = languageGazetteer.of(
keyword.replace("loc.language.", "")
);
}
} else if (keyword.match(/^loc\.country\./)) {
const localGazetteer = new Intl.DisplayNames(
[...(grimm.dict?.[locale]?.meta?.intlFallback ?? locale)],
{
type: "region"
}
);
if (
keyword.replace("loc.country.", "").length == 2 &&
localGazetteer.of(
keyword.replace("loc.country.", "").toUpperCase()
) != keyword.replace("loc.country.", "").toUpperCase()
) {
gazettedName = localGazetteer.of(
keyword.replace("loc.country.", "").toUpperCase()
);
}
}
const firstWorkingFallback = [
locale,
...(grimm.dict?.[locale]?.meta?.fallback ?? []),
"default"
].find(
el => getNestedObject(grimm.dict?.[el], keywordArray) !== undefined
);
const fallbackResult = firstWorkingFallback
? getNestedObject(grimm.dict?.[firstWorkingFallback], keywordArray)
: undefined;
return fallbackResult === undefined
? gazettedName || `[${keyword}]`
: typeof fallbackResult == "function" && args.length
? fallbackResult(...args)
: fallbackResult;
},
translator:
locale =>
(...args) =>
grimm.translate(locale, ...args),
num: (locale, x) =>
grimm.dict?.[locale]?.help.num
? grimm.dict[locale].help.num(x)
: new Intl.NumberFormat(
grimm.dict?.[locale]?.meta?.intlFallback ?? locale
).format(x),
relativeDate: (locale, ms) => {
let formatter = new Intl.RelativeTimeFormat(
grimm.dict?.[locale]?.meta?.intlFallback ?? locale
);
if (Math.abs(ms) >= 365.2425 * 24 * 60 * 60 * 1000) {
return formatter.format(
Math.round(ms / (365.2425 * 24 * 60 * 60 * 1000)),
"year"
);
}
if (Math.abs(ms) >= (365.2425 / 12) * 24 * 60 * 60 * 1000) {
return formatter.format(
Math.round(ms / ((365.2425 / 12) * 24 * 60 * 60 * 1000)),
"month"
);
}
if (Math.abs(ms) >= 7 * 24 * 60 * 60 * 1000) {
return formatter.format(
Math.round(ms / (7 * 24 * 60 * 60 * 1000)),
"week"
);
}
if (Math.abs(ms) >= 24 * 60 * 60 * 1000) {
return formatter.format(
Math.round(ms / (24 * 60 * 60 * 1000)),
"day"
);
}
if (Math.abs(ms) >= 60 * 60 * 1000) {
return formatter.format(Math.round(ms / (60 * 60 * 1000)), "hour");
}
if (Math.abs(ms) >= 60 * 1000) {
return formatter.format(Math.round(ms / (60 * 1000)), "minute");
}
return formatter.format(Math.round(ms / 1000), "second");
},
date: {
ce: (locale, stamp, precision, inputOptions = {}) => {
let date;
if (typeof stamp == "string") {
const matched = stamp.match(
/(c)?([+-]?[0-9]+)(?:-([0-9]{2}))?(?:-([0-9]{2}))?(?:T([0-9]{2}):?([0-9]{2})?:?([0-9]{2})?(\.[0-9]+)?)?(Z|[+-][0-9]{2}(?::?[0-9]{2})?)?/
);
const stampHasDashes =
matched[2].match(/[+-]/) || matched[2].length != 8;
date = {
circa: matched[1] ? true : false,
year: stampHasDashes
? parseInt(matched[2])
: parseInt(matched[2].slice(0, 4)),
month: stampHasDashes
? matched[3]
? parseInt(matched[3])
: 6
: parseInt(matched[2].slice(4, 6)),
day: stampHasDashes
? matched[4]
? parseInt(matched[4])
: 3
: parseInt(matched[2].slice(6, 8)),
hour: matched[5] ? parseInt(matched[5]) : 12,
minute: matched[6] ? parseInt(matched[6]) : 0,
second: matched[7] ? parseInt(matched[7]) : 0,
millisecond: matched[8]
? Math.round(1000 * parseFloat(matched[8]))
: 0,
timezone: matched[9]
? matched[9]
.replace(
/^([+-])([0-9]{2})([0-9]{2})$/,
"$1$2:$3"
)
.replace(/^([+-])([0-9]{2})$/, "$1$2:00")
: "",
stampPrecision: matched.slice(2).includes(undefined)
? matched.slice(2).indexOf(undefined)
: 7
};
date.stamp = `${Math.max(date.year, 1)
.toString()
.padStart(4, "0")}-${date.month
.toString()
.padStart(2, "0")}-${date.day
.toString()
.padStart(2, "0")}T${date.hour
.toString()
.padStart(2, "0")}:${date.minute
.toString()
.padStart(2, "0")}:${date.second
.toString()
.padStart(2, "0")}.${date.millisecond
.toString()
.padStart(3, "0")}${date.timezone}`;
date.date = new Date(date.stamp);
if (date.year < 1) {
date.date.setYear(date.year);
}
precision = Math.min(
date.stampPrecision,
precision ?? Infinity
);
} else {
date = {
circa: false,
date: stamp
};
precision = inputOptions.precision ?? Infinity;
}
const options = {
year: "numeric",
month: precision >= 2 ? "long" : undefined,
day: precision >= 3 ? "2-digit" : undefined,
hour: precision >= 4 ? "2-digit" : undefined,
minute: precision >= 5 ? "2-digit" : undefined,
second: precision >= 6 ? "2-digit" : undefined,
hour12: false,
timeZone: "Europe/London"
};
Object.assign(options, inputOptions);
const dateFormatter = grimm.dict?.[locale]?.help?.CustomDate
? new grimm.dict[locale].help.CustomDate(options)
: new Intl.DateTimeFormat(
grimm.dict?.[locale]?.meta?.intlFallback ?? locale,
options
);
let returnDate = dateFormatter.format(date.date);
if (date.circa) {
returnDate = grimm.translate(locale, "pan.circa", returnDate);
}
return returnDate;
},
attic: (locale, date, inputOptions = {}) => {
let options = {
dateStyle: "long",
numerals: "greek",
signedDates: true
};
Object.assign(options, inputOptions);
const obj = attic(date);
let dateNumber = "";
if (options.signedDates) {
dateNumber = obj.signedDate
? `${grimm.enumerate(
Math.abs(obj.signedDate),
options.numerals
)}${
grimm.translate(locale, "attic.moons")[
Math.sign(obj.signedDate) + 1
]
}`
: grimm.translate(locale, "attic.moons")[1];
} else {
dateNumber = grimm.enumerate(obj.rawDate, options.numerals);
}
return grimm.translate(
locale,
`attic.template.${options.dateStyle}`,
grimm.enumerate(obj.olympiad, options.numerals),
grimm.enumerate(obj.year, options.numerals),
grimm.translate(locale, `attic.months.${options.dateStyle}`)[
obj.month
],
dateNumber
);
},
dual: (locale, date) =>
`${grimm.date.attic(locale, date)}${grimm.translate(
locale,
"pan.dateSep"
)}${grimm.date.ce(locale, date, 3)}`
}
};
grimm.dict = {
default: defaultLocale,
en: enLocale(grimm),
nl: nlLocale(grimm),
grc: grcLocale(grimm),
cy: cyLocale(grimm),
pl: plLocale(grimm),
eo: eoLocale(grimm),
"zh-Hant": zhHantLocale(grimm),
zh: zhLocale(grimm),
ar: {
meta: {
id: "ar",
code: "ar-SA",
name: "العربية",
sort: "arabiyyah",
flag: "arableague",
fallback: ["en"],
intlFallback: ["ar-SA"]
}
},
es: {
meta: {
id: "es",
code: "es-ES",
name: "Castellano",
sort: "castellano",
flag: "es",
fallback: ["en"],
intlFallback: ["es-ES"]
}
},
de: {
meta: {
id: "de",
code: "de-DE",
name: "Deutsch",
sort: "deutsch",
flag: "de",
fallback: ["en"],
intlFallback: ["de-DE"]
}
},
el: {
meta: {
id: "el",
code: "el-GR",
name: "Ελληνικά",
sort: "ellinika",
flag: "gr",
fallback: ["grc", "en"],
intlFallback: ["el-GR"]
}
},
fr: {
meta: {
id: "fr",
code: "fr-FR",
name: "Français",
sort: "francais",
flag: "fr",
fallback: ["en"],
intlFallback: ["fr-FR"]
}
},
hi: {
meta: {
id: "hi",
code: "hi-IN",
name: "हिन्दी",
sort: "hindi",
flag: "in",
fallback: ["en"],
intlFallback: ["hi-IN"]
}
},
sw: {
meta: {
id: "sw",
code: "sw-TZ",
name: "Kiswahili",
sort: "kiswahili",
flag: "tz",
fallback: ["en"],
intlFallback: ["sw-TZ", "en"]
}
},
ja: {
meta: {
id: "ja",
code: "ja-JP",
name: "日本語",
sort: "nihongo",
flag: "jp",
fallback: ["en"],
intlFallback: ["ja-JP"]
}
}
};
export default grimm;