-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
24 changed files
with
653 additions
and
150 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
tsconfig.json | ||
src |
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,28 @@ | ||
interface Formats { | ||
number: Record<string, any>; | ||
date: Record<string, any>; | ||
time: Record<string, any>; | ||
} | ||
interface Options { | ||
fallbackLocale: string; | ||
initialLocale: string; | ||
formats: Formats; | ||
loadingDelay: number; | ||
warnOnMissingMessages: boolean; | ||
} | ||
interface GetClientLocaleOptions { | ||
navigator?: boolean; | ||
hash?: string; | ||
search?: string; | ||
pathname?: RegExp; | ||
hostname?: RegExp; | ||
} | ||
interface ConfigureOptions { | ||
fallbackLocale: string; | ||
initialLocale?: string | GetClientLocaleOptions; | ||
formats?: Partial<Formats>; | ||
loadingDelay?: number; | ||
} | ||
export declare function init(opts: ConfigureOptions): void; | ||
export declare function getOptions(): Options; | ||
export {}; |
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,111 @@ | ||
import { currentLocale } from "./stores"; | ||
const defaultFormats = { | ||
number: { | ||
scientific: { notation: "scientific" }, | ||
engineering: { notation: "engineering" }, | ||
compactLong: { notation: "compact", compactDisplay: "long" }, | ||
compactShort: { notation: "compact", compactDisplay: "short" } | ||
}, | ||
date: { | ||
short: { month: "numeric", day: "numeric", year: "2-digit" }, | ||
medium: { month: "short", day: "numeric", year: "numeric" }, | ||
long: { month: "long", day: "numeric", year: "numeric" }, | ||
full: { weekday: "long", month: "long", day: "numeric", year: "numeric" } | ||
}, | ||
time: { | ||
short: { hour: "numeric", minute: "numeric" }, | ||
medium: { hour: "numeric", minute: "numeric", second: "numeric" }, | ||
long: { | ||
hour: "numeric", | ||
minute: "numeric", | ||
second: "numeric", | ||
timeZoneName: "short" | ||
}, | ||
full: { | ||
hour: "numeric", | ||
minute: "numeric", | ||
second: "numeric", | ||
timeZoneName: "short" | ||
} | ||
} | ||
}; | ||
const defaultOptions = { | ||
fallbackLocale: null, | ||
initialLocale: null, | ||
loadingDelay: 200, | ||
formats: defaultFormats, | ||
warnOnMissingMessages: true | ||
}; | ||
const options = defaultOptions; | ||
const getFromQueryString = (queryString, key) => { | ||
const keyVal = queryString.split("&").find(i => i.indexOf(`${key}=`) === 0); | ||
if (keyVal) { | ||
return keyVal.split("=").pop(); | ||
} | ||
return null; | ||
}; | ||
const getFirstMatch = (base, pattern) => { | ||
const match = pattern.exec(base); | ||
// istanbul ignore if | ||
if (!match) | ||
return null; | ||
// istanbul ignore else | ||
return match[1] || null; | ||
}; | ||
function getClientLocale({ navigator, hash, search, pathname, hostname }) { | ||
let locale; | ||
// istanbul ignore next | ||
if (typeof window === "undefined") | ||
return null; | ||
if (hostname) { | ||
locale = getFirstMatch(window.location.hostname, hostname); | ||
if (locale) | ||
return locale; | ||
} | ||
if (pathname) { | ||
locale = getFirstMatch(window.location.pathname, pathname); | ||
if (locale) | ||
return locale; | ||
} | ||
if (navigator) { | ||
// istanbul ignore else | ||
locale = window.navigator.language || window.navigator.languages[0]; | ||
if (locale) | ||
return locale; | ||
} | ||
if (search) { | ||
locale = getFromQueryString(window.location.search.substr(1), search); | ||
if (locale) | ||
return locale; | ||
} | ||
if (hash) { | ||
locale = getFromQueryString(window.location.hash.substr(1), hash); | ||
if (locale) | ||
return locale; | ||
} | ||
return null; | ||
} | ||
export function init(opts) { | ||
const { formats, ...rest } = opts; | ||
const initialLocale = opts.initialLocale | ||
? typeof opts.initialLocale === "string" | ||
? opts.initialLocale | ||
: getClientLocale(opts.initialLocale) || opts.fallbackLocale | ||
: opts.fallbackLocale; | ||
Object.assign(options, rest, { initialLocale }); | ||
if (formats) { | ||
if ("number" in formats) { | ||
Object.assign(options.formats.number, formats.number); | ||
} | ||
if ("date" in formats) { | ||
Object.assign(options.formats.date, formats.date); | ||
} | ||
if ("time" in formats) { | ||
Object.assign(options.formats.time, formats.time); | ||
} | ||
} | ||
return currentLocale.set(initialLocale); | ||
} | ||
export function getOptions() { | ||
return options; | ||
} |
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,14 @@ | ||
declare type IntlFormatterOptions<T> = T & { | ||
format?: string; | ||
locale?: string; | ||
}; | ||
interface MemoizedIntlFormatter<T, U> { | ||
(options?: IntlFormatterOptions<U>): T; | ||
} | ||
export declare const getNumberFormatter: MemoizedIntlFormatter<Intl.NumberFormat, Intl.NumberFormatOptions>; | ||
export declare const getDateFormatter: MemoizedIntlFormatter<Intl.DateTimeFormat, Intl.DateTimeFormatOptions>; | ||
export declare const getTimeFormatter: MemoizedIntlFormatter<Intl.DateTimeFormat, Intl.DateTimeFormatOptions>; | ||
export declare const formatTime: (t: Date, options: Record<string, any>) => string; | ||
export declare const formatDate: (d: Date, options: Record<string, any>) => string; | ||
export declare const formatNumber: (n: number, options: Record<string, any>) => string; | ||
export {}; |
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,124 @@ | ||
import { getCurrentLocale } from './stores'; | ||
import { getOptions } from './config'; | ||
import { monadicMemoize } from './memoize'; | ||
const getIntlFormatterOptions = (type, name) => { | ||
const formats = getOptions().formats; | ||
if (type in formats && name in formats[type]) { | ||
return formats[type][name]; | ||
} | ||
throw new Error(`[icu-helpers] Unknown "${name}" ${type} format.`); | ||
}; | ||
export const getNumberFormatter = monadicMemoize(({ locale, format, ...options } = {}) => { | ||
locale = locale || getCurrentLocale(); | ||
if (locale == null) { | ||
throw new Error('[icu-helpers] A "locale" must be set to format numbers'); | ||
} | ||
if (format) { | ||
options = getIntlFormatterOptions('number', format); | ||
} | ||
return new Intl.NumberFormat(locale, options); | ||
}); | ||
export const getDateFormatter = monadicMemoize(({ locale, format, ...options } = {}) => { | ||
locale = locale || getCurrentLocale(); | ||
if (locale == null) { | ||
throw new Error('[icu-helpers] A "locale" must be set to format dates'); | ||
} | ||
if (format) | ||
options = getIntlFormatterOptions('date', format); | ||
else if (Object.keys(options).length === 0) { | ||
options = getIntlFormatterOptions('date', 'short'); | ||
} | ||
return new Intl.DateTimeFormat(locale, options); | ||
}); | ||
export const getTimeFormatter = monadicMemoize(({ locale, format, ...options } = {}) => { | ||
locale = locale || getCurrentLocale(); | ||
if (locale == null) { | ||
throw new Error('[svelte-i18n] A "locale" must be set to format time values'); | ||
} | ||
if (format) | ||
options = getIntlFormatterOptions('time', format); | ||
else if (Object.keys(options).length === 0) { | ||
options = getIntlFormatterOptions('time', 'short'); | ||
} | ||
return new Intl.DateTimeFormat(locale, options); | ||
}); | ||
export const formatTime = (t, options) => getTimeFormatter(options).format(t); | ||
export const formatDate = (d, options) => getDateFormatter(options).format(d); | ||
export const formatNumber = (n, options) => getNumberFormatter(options).format(n); | ||
// import { getOptions } from "./config"; | ||
// const CACHED = Object.create(null); | ||
// export function formatterOptions(type, style) { | ||
// return getOptions().formats[type][style] || {}; | ||
// } | ||
// const getIntlFormatterOptions = (type, name) => { | ||
// const formats = getOptions().formats | ||
// if (type in formats && name in formats[type]) { | ||
// return formats[type][name] | ||
// } | ||
// throw new Error(`[icu-helpers] Unknown "${name}" ${type} format.`) | ||
// } | ||
// // const getIntlFormatterOptions = ( | ||
// // type: 'time' | 'number' | 'date', | ||
// // name: string | ||
// // ): any => { | ||
// // const formats = getOptions().formats | ||
// // if (type in formats && name in formats[type]) { | ||
// // return formats[type][name] | ||
// // } | ||
// // throw new Error(`[svelte-i18n] Unknown "${name}" ${type} format.`) | ||
// // } | ||
// // export const getNumberFormatter: MemoizedIntlFormatter< | ||
// // Intl.NumberFormat, | ||
// // Intl.NumberFormatOptions | ||
// // > = monadicMemoize(({ locale, format, ...options } = {}) => { | ||
// // locale = locale || getCurrentLocale() | ||
// // if (locale == null) { | ||
// // throw new Error('[svelte-i18n] A "locale" must be set to format numbers') | ||
// // } | ||
// // if (format) { | ||
// // options = getIntlFormatterOptions('number', format) | ||
// // } | ||
// // return new Intl.NumberFormat(locale, options) | ||
// // }) | ||
// // export const getDateFormatter: MemoizedIntlFormatter< | ||
// // Intl.DateTimeFormat, | ||
// // Intl.DateTimeFormatOptions | ||
// // > = monadicMemoize(({ locale, format, ...options } = {}) => { | ||
// // locale = locale || getCurrentLocale() | ||
// // if (locale == null) { | ||
// // throw new Error('[svelte-i18n] A "locale" must be set to format dates') | ||
// // } | ||
// // if (format) options = getIntlFormatterOptions('date', format) | ||
// // else if (Object.keys(options).length === 0) { | ||
// // options = getIntlFormatterOptions('date', 'short') | ||
// // } | ||
// // return new Intl.DateTimeFormat(locale, options) | ||
// // }) | ||
// // export const getTimeFormatter: MemoizedIntlFormatter< | ||
// // Intl.DateTimeFormat, | ||
// // Intl.DateTimeFormatOptions | ||
// // > = monadicMemoize(({ locale, format, ...options } = {}) => { | ||
// // locale = locale || getCurrentLocale() | ||
// // if (locale == null) { | ||
// // throw new Error( | ||
// // '[svelte-i18n] A "locale" must be set to format time values' | ||
// // ) | ||
// // } | ||
// // if (format) options = getIntlFormatterOptions('time', format) | ||
// // else if (Object.keys(options).length === 0) { | ||
// // options = getIntlFormatterOptions('time', 'short') | ||
// // } | ||
// // return new Intl.DateTimeFormat(locale, options) | ||
// // }) | ||
// // export function getNumberFormatter({ locale, format, ...options }) { | ||
// // let key = "number" + locale + JSON.stringify(opts); | ||
// // return CACHED[key] || (CACHED[key] = new Intl.NumberFormat(locale, opts)); | ||
// // } | ||
// // export function getDateFormatter(locale, opts) { | ||
// // let key = "date" + locale + JSON.stringify(opts); | ||
// // return CACHED[key] || (CACHED[key] = new Intl.DateTimeFormat(locale, opts)); | ||
// // } | ||
// // export function getTimeFormatter(locale, opts) { | ||
// // let key = "time" + locale + JSON.stringify(opts); | ||
// // return CACHED[key] || (CACHED[key] = new Intl.DateTimeFormat(locale, opts)); | ||
// // } |
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,14 @@ | ||
import { init } from "./config"; | ||
import { currentLocale, dictionary, locales, LocaleDictionary } from "./stores"; | ||
import { getNumberFormatter, getDateFormatter, getTimeFormatter, formatTime, formatDate, formatNumber } from "./formatters"; | ||
declare type PluralRule = "zero" | "one" | "two" | "few" | "many" | "other" | number; | ||
declare type PluralOptions = Record<PluralRule, string>; | ||
export declare function __interpolate(value: any): any; | ||
export declare function __plural(value: number, offsetOrOptions: number | PluralOptions, opts?: PluralOptions): string; | ||
export declare function __select(value: any, opts: Record<any, string>): string; | ||
export declare function __number(value: number, format?: string): string; | ||
export declare function __date(value: Date, format?: string): string; | ||
export declare function __time(value: Date, format?: string): string; | ||
export declare function addMessages(locale: string, messages: LocaleDictionary): void; | ||
export declare function lookupMessage(key: string, locale?: string): string; | ||
export { init, currentLocale, dictionary, locales, getNumberFormatter, getDateFormatter, getTimeFormatter, formatTime, formatDate, formatNumber }; |
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,46 @@ | ||
import { init } from "./config"; | ||
import { currentLocale, dictionary, locales, getCurrentLocale } from "./stores"; | ||
import { getNumberFormatter, getDateFormatter, getTimeFormatter, formatTime, formatDate, formatNumber } from "./formatters"; | ||
export function __interpolate(value) { | ||
return value === 0 ? 0 : value || ''; | ||
} | ||
const PLURAL_RULES = Object.create(null); | ||
function getLocalPluralFor(v) { | ||
let loc = getCurrentLocale(); | ||
let pluralRules = PLURAL_RULES[loc] || (PLURAL_RULES[loc] = new Intl.PluralRules(loc)); | ||
return pluralRules.select(v); | ||
} | ||
export function __plural(value, offsetOrOptions, opts) { | ||
if (typeof offsetOrOptions === "number") { | ||
return (opts[value] || | ||
opts[getLocalPluralFor(value - offsetOrOptions)] || | ||
""); | ||
} | ||
else { | ||
return (offsetOrOptions[value] || | ||
offsetOrOptions[getLocalPluralFor(value)] || | ||
""); | ||
} | ||
} | ||
export function __select(value, opts) { | ||
return opts[value] || opts['other'] || ''; | ||
} | ||
export function __number(value, format) { | ||
return formatNumber(value, { format }); | ||
} | ||
export function __date(value, format = "short") { | ||
return formatDate(value, { format }); | ||
} | ||
export function __time(value, format = "short") { | ||
return formatTime(value, { format }); | ||
} | ||
export function addMessages(locale, messages) { | ||
dictionary.update(value => { | ||
value[locale] = Object.assign(value[locale] || {}, messages); | ||
return value; | ||
}); | ||
} | ||
export function lookupMessage(key, locale = getCurrentLocale()) { | ||
return dictionary._value[locale][key]; | ||
} | ||
export { init, currentLocale, dictionary, locales, getNumberFormatter, getDateFormatter, getTimeFormatter, formatTime, formatDate, formatNumber }; |
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,3 @@ | ||
declare type MemoizedFunction = <F extends any>(fn: F) => F; | ||
declare const monadicMemoize: MemoizedFunction; | ||
export { monadicMemoize }; |
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,12 @@ | ||
const monadicMemoize = fn => { | ||
const cache = Object.create(null); | ||
const memoizedFn = (arg) => { | ||
const cacheKey = JSON.stringify(arg); | ||
if (cacheKey in cache) { | ||
return cache[cacheKey]; | ||
} | ||
return (cache[cacheKey] = fn(arg)); | ||
}; | ||
return memoizedFn; | ||
}; | ||
export { monadicMemoize }; |
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,14 @@ | ||
declare class WritableStore<T> { | ||
_value: T; | ||
_subscribers: ((T: any) => void)[]; | ||
constructor(v: any); | ||
subscribe(fn: (value: T) => void): () => ((T: any) => void)[]; | ||
set(v: T): void; | ||
update(cb: (value: T) => T): void; | ||
} | ||
export declare type LocaleDictionary = Record<string, string>; | ||
export declare const currentLocale: WritableStore<string>; | ||
export declare const dictionary: WritableStore<Record<string, Record<string, string>>>; | ||
export declare const locales: WritableStore<string[]>; | ||
export declare function getCurrentLocale(): string; | ||
export {}; |
Oops, something went wrong.