cronofy-elements
Advanced tools
Comparing version 1.34.8 to 1.35.0
{ | ||
"name": "cronofy-elements", | ||
"version": "1.34.8", | ||
"version": "1.35.0", | ||
"description": "Fast track scheduling with Cronofy's embeddable UI Elements", | ||
@@ -5,0 +5,0 @@ "main": "build/npm/CronofyElements.js", |
@@ -80,2 +80,3 @@ import React, { useState } from "react"; | ||
locale: options.locale, | ||
locale_modifiers: options.locale_modifiers, | ||
slug: "date_time_picker", | ||
@@ -82,0 +83,0 @@ allPhrases: options.translations, |
@@ -27,3 +27,3 @@ import React from "react"; | ||
<div className={theme.classBuilder("time-slots-list--header")}> | ||
<h3>{i18n.ftz(moment(status.selectedDay, "YYYY-MM-DD"), "dddd Do MMMM")}</h3> | ||
<h3>{i18n.dateToSlotString(status.selectedDay)}</h3> | ||
</div> | ||
@@ -30,0 +30,0 @@ <div className={theme.classBuilder("time-slots-group--wrapper")}> |
@@ -49,3 +49,3 @@ import React, { useContext } from "react"; | ||
> | ||
<span>{period.label}</span> | ||
<span>{i18n.dateToSlotString(period.value)}</span> | ||
</button> | ||
@@ -52,0 +52,0 @@ ) : ( |
@@ -37,2 +37,3 @@ import React, { useState, useEffect } from "react"; | ||
locale: options.locale, | ||
localeModifiers: options.locale_modifiers, | ||
allPhrases: options.translations, | ||
@@ -39,0 +40,0 @@ tzid: options.tzid, |
@@ -75,3 +75,3 @@ import React, { useContext } from "react"; | ||
> | ||
{i18n.f(moment(slots.day, "YYYY-MM-DD"), `dddd Do MMMM`)} | ||
{i18n.dateToSlotString(moment(slots.day, "YYYY-MM-DD"))} | ||
</span> | ||
@@ -78,0 +78,0 @@ </div> |
@@ -12,2 +12,3 @@ import React, { createContext, useContext, useState } from "react"; | ||
locale: options.locale, | ||
localeModifiers: options.locale_modifiers, | ||
allPhrases: options.allPhrases, | ||
@@ -14,0 +15,0 @@ tzid: options.tzid, |
@@ -1,3 +0,4 @@ | ||
import { turnSlugIntoString, humanizeTzName } from "./utils"; | ||
import { turnSlugIntoString, humanizeTzName, nestedValue } from "./utils"; | ||
import { translations } from "./translations"; | ||
import moment from "moment"; | ||
@@ -9,2 +10,13 @@ export const getNavigatorLanguage = (nav = navigator) => | ||
const rootLocaleFromLocale = locale => | ||
locale.indexOf("-") > 0 ? locale.substr(0, locale.indexOf("-")) : locale; | ||
export const modifierDateFormats = { | ||
ja: { | ||
dates: { | ||
western: { slotFormat: "Y年 MMM Do (dd)" }, | ||
}, | ||
}, | ||
}; | ||
export const i18n = ( | ||
@@ -21,4 +33,4 @@ context = "core", | ||
// Parse the locale (if it's one we want to normalize) | ||
locale = locale === "en-US" ? "en" : locale; | ||
// get the root locale if using a dialect | ||
const rootLocale = rootLocaleFromLocale(locale); | ||
@@ -28,10 +40,27 @@ // 1. Pre-check fallbacks | ||
if (typeof allPhrases[locale] === "undefined" && locale !== "keys") { | ||
// Fallback to base locale | ||
locale = "en"; | ||
if (typeof allPhrases[locale] === "undefined") { | ||
//First check if phrase exists in rootLocale | ||
if (typeof allPhrases[rootLocale] !== "undefined") { | ||
//Fallback to rootLocale if it does | ||
locale = rootLocale; | ||
} else { | ||
// Fallback to base locale if not | ||
locale = "en"; | ||
} | ||
} | ||
if (typeof allPhrases[locale][context] === "undefined" && context === "date_formats") { | ||
// Fallback to the format in the 'en' date formats | ||
return allPhrases["en"][context][phrase]; | ||
} | ||
if (typeof allPhrases[locale][context] === "undefined") { | ||
// Fallback to base context | ||
context = "core"; | ||
//First check if context exists in the rootLocale | ||
if (typeof allPhrases[rootLocale][context] !== "undefined") { | ||
//Fallback to rootLocale if it does | ||
locale = rootLocale; | ||
} else { | ||
// Fallback to base context if it doesn't | ||
context = "core"; | ||
} | ||
} | ||
@@ -42,5 +71,2 @@ | ||
return allPhrases[locale][context][phrase]; | ||
// 2. Post-check fallbacks | ||
// ...we have the provided context and locale, but the phrase can't be found. | ||
} else if (typeof allPhrases[locale]["core"][phrase] !== "undefined") { | ||
@@ -55,3 +81,5 @@ // Check the phrase with our base context: | ||
// highlight the error but not break the app. | ||
console.warn(`Missing translation. slug: ${phrase}, context: ${context}`); | ||
console.warn( | ||
`Missing translation. slug: ${phrase}, context: ${context}, locale: ${locale}` | ||
); | ||
return turnSlugIntoString(phrase); | ||
@@ -92,10 +120,35 @@ } | ||
locale = getNavigatorLanguage(), | ||
localeModifiers = {}, | ||
allPhrases = translations, | ||
tzid, | ||
}) => ({ | ||
t: slug => i18n(context, slug, locale, allPhrases), | ||
f: (momentObject, formatString) => momentObject.locale(locale).format(formatString), | ||
ftz: (momentObject, formatString) => momentObject.tz(tzid).locale(locale).format(formatString), | ||
customFormatedTimeZone: (momentObject, ctz, formatString) => | ||
momentObject.tz(ctz).locale(locale).format(formatString), | ||
}); | ||
}) => { | ||
const slotFormatString = () => { | ||
let slotFormat = "dddd Do MMMM"; | ||
const configVariant = nestedValue([locale, "dates"], localeModifiers); | ||
if (configVariant) { | ||
const overrideFormat = nestedValue( | ||
[locale, "dates", configVariant, "slotFormat"], | ||
modifierDateFormats | ||
); | ||
if (overrideFormat) slotFormat = overrideFormat; | ||
} | ||
return slotFormat; | ||
}; | ||
const formatDateString = (dateString, formatString) => | ||
moment(dateString).locale(locale).format(formatString); | ||
const dateToSlotString = dateString => formatDateString(dateString, slotFormatString()); | ||
return { | ||
t: slug => i18n(context, slug, locale, allPhrases), | ||
f: formatDateString, | ||
ftz: (momentObject, formatString) => | ||
momentObject.tz(tzid).locale(locale).format(formatString), | ||
customFormatedTimeZone: (momentObject, ctz, formatString) => | ||
momentObject.tz(ctz).locale(locale).format(formatString), | ||
dateToSlotString: dateToSlotString, | ||
}; | ||
}; |
@@ -8,2 +8,3 @@ import { | ||
parseTranslations, | ||
validateLocaleModifiers, | ||
} from "./init"; | ||
@@ -87,2 +88,11 @@ import { logConstructor } from "./logging"; | ||
const modifier = | ||
typeof options.locale_modifiers !== "undefined" ? options.locale_modifiers : null; | ||
const isValidLocaleModifiers = modifier ? validateLocaleModifiers(modifier) : true; | ||
if (!isValidLocaleModifiers) { | ||
log.warn("`locale_modifiers is invalid. Modifiers will fallback to the default.`", { | ||
docsSlug: "#locale-modifiers", | ||
}); | ||
} | ||
delete options.availability_query; | ||
@@ -89,0 +99,0 @@ delete options.element_token; |
import merge from "deepmerge"; | ||
import { translations } from "./translations"; | ||
import moment from "moment-timezone"; | ||
import { modifierDateFormats } from "./i18n"; | ||
import { nestedValue } from "./utils"; | ||
@@ -284,1 +286,24 @@ export const parseConnectionDomains = ( | ||
}; | ||
export const validateLocaleModifiers = modifiers => { | ||
const defaultLocales = Object.keys(modifierDateFormats); | ||
const locales = Object.keys(modifiers); | ||
for (const locale of locales) { | ||
const test = defaultLocales.includes(locale); | ||
if (!test) { | ||
return false; | ||
} | ||
} | ||
for (const locale of locales) { | ||
const value = nestedValue([locale, "dates"], modifiers); | ||
if (value) { | ||
const valueA = nestedValue([locale, "dates"], modifiers); | ||
const valueB = nestedValue([locale, "dates"], modifierDateFormats); | ||
return valueB[valueA] ? true : false; | ||
} else { | ||
return false; | ||
} | ||
} | ||
}; |
@@ -8,2 +8,3 @@ import { | ||
parseTranslations, | ||
validateLocaleModifiers, | ||
} from "./init"; | ||
@@ -61,2 +62,15 @@ import { logConstructor } from "./logging"; | ||
const modifier = | ||
typeof options.locale_modifiers !== "undefined" ? options.locale_modifiers : null; | ||
const isValidLocaleModifiers = modifier ? validateLocaleModifiers(modifier) : true; | ||
if (!isValidLocaleModifiers) { | ||
log.warn( | ||
"`locale_modifiers has an invalid value. Invalid modifiers will fallback to the default.`", | ||
{ | ||
docsSlug: "#locale-modifiers", | ||
} | ||
); | ||
} | ||
delete options.availability_query; | ||
@@ -63,0 +77,0 @@ delete options.element_token; |
@@ -768,3 +768,3 @@ import moment from "moment-timezone"; | ||
query_periods: [{ start: offsetTime(1, "09:00"), end: offsetTime(9, "11:30") }], | ||
required_duration: { minutes: 60 }, | ||
required_duration: { minutes: 30 }, | ||
}; |
@@ -1,18 +0,31 @@ | ||
const getTranslationsFromJson = (locales, contexts) => | ||
locales.reduce( | ||
(acc, locale) => ({ | ||
...acc, | ||
[locale]: contexts.reduce((acc, context) => { | ||
const { | ||
[context]: contextObj, | ||
} = require(`../translations/${locale}/${context}.json`); | ||
return { | ||
...acc, | ||
[context]: contextObj, | ||
}; | ||
}, {}), | ||
}), | ||
{} | ||
); | ||
const requireTranslationFile = (locale, context) => { | ||
try { | ||
return require(`../translations/${locale}/${context}.json`); | ||
} catch (e) { | ||
// We don't expect a file for every context - some things fallback | ||
return false; | ||
} | ||
}; | ||
const getTranslationsFromJson = (locales, contexts) => { | ||
let translations = {}; | ||
locales.forEach(locale => { | ||
contexts.forEach(context => { | ||
const loaded = requireTranslationFile(locale, context); | ||
if (loaded) { | ||
if (!translations[locale]) { | ||
translations[locale] = {}; | ||
} | ||
translations[locale][context] = loaded[context]; | ||
} | ||
}); | ||
}); | ||
return translations; | ||
}; | ||
const locales = ["de", "en", "en-US", "es", "fr", "fr-CA", "it", "ja", "nl", "ru", "sv"]; | ||
const contexts = [ | ||
@@ -28,4 +41,3 @@ "core", | ||
]; | ||
const locales = ["de", "en", "es", "fr", "fr-CA", "it", "ja", "nl", "ru", "sv"]; | ||
export const translations = getTranslationsFromJson(locales, contexts); |
@@ -69,1 +69,3 @@ import { compose } from "./functional"; | ||
export const humanizeTzName = tzid => tzid.split("/").slice(-1)[0].replace(/_/g, " "); | ||
export const nestedValue = (path, obj) => path.reduce((xs, x) => (xs && xs[x] ? xs[x] : null), obj); |
import * as i18n from "../src/js/helpers/i18n"; | ||
import moment from "moment"; | ||
@@ -140,2 +141,73 @@ describe("i18n", () => { | ||
}); | ||
it("fallsback to the base language when the locale is a dialect without a translation", () => { | ||
const testPhrases = { | ||
en: { | ||
core: { stairs: "stairs", choux_bun: "choux bun" }, | ||
}, | ||
"en-COCKNEY": { | ||
core: { stairs: "apples and pears" }, | ||
}, | ||
}; | ||
const dialectExists = i18n.i18n("core", "stairs", "en-COCKNEY", testPhrases); | ||
expect(dialectExists).toEqual("apples and pears"); | ||
const fallbackRequired = i18n.i18n("core", "choux_bun", "en-COCKNEY", testPhrases); | ||
expect(fallbackRequired).toEqual("choux bun"); | ||
}); | ||
}); | ||
describe("i18n.dateToSlotString", () => { | ||
const testTime = moment("2021-08-12T10:00:00Z"); | ||
it("EN formats the time into a reasonable format", () => { | ||
const translations = i18n.i18nConstructor({ locale: "en" }); | ||
const formatted = translations.dateToSlotString(testTime); | ||
expect(formatted).toEqual("Thursday 12th August"); | ||
}); | ||
it("translates the elements in the EN format", () => { | ||
const translations = i18n.i18nConstructor({ locale: "de" }); | ||
const formatted = translations.dateToSlotString(testTime); | ||
expect(formatted).toEqual("Donnerstag 12. August"); | ||
}); | ||
describe("when constructed using some locale_modifiers for the current locale", () => { | ||
it("returns the modified variant of the date format", () => { | ||
const translations = i18n.i18nConstructor({ | ||
locale: "ja", | ||
localeModifiers: { ja: { dates: "western" } }, | ||
}); | ||
const formatted = translations.dateToSlotString(testTime); | ||
expect(formatted).toEqual("2021年 8月 12日 (木)"); | ||
}); | ||
}); | ||
describe("when constructed using some locale_modifiers for another locale", () => { | ||
it("returns the modified variant of the date format", () => { | ||
const translations = i18n.i18nConstructor({ | ||
locale: "en", | ||
localeModifiers: { ja: { dates: "western" } }, | ||
}); | ||
const formatted = translations.dateToSlotString(testTime); | ||
expect(formatted).toEqual("Thursday 12th August"); | ||
}); | ||
}); | ||
describe("when constructed with invalid locale_modifiers", () => { | ||
it("returns a the default date format", () => { | ||
const translations = i18n.i18nConstructor({ | ||
locale: "ja", | ||
localeModifiers: { ja: { dates: "asterley" } }, | ||
}); | ||
const formatted = translations.dateToSlotString(testTime); | ||
expect(formatted).toEqual("木曜日 12日 8月"); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
308
23617
3471901