create-dan
Version:
Dan frameworks
192 lines (162 loc) • 4.55 kB
JavaScript
import React from 'react'
import fetch from 'isomorphic-unfetch'
import getConfig from 'config'
const DEFAULT_FILES = ['main']
// Cache
const i18nFilesCache = {}
/**
* Cache i18n files
* @param {Object} i18n
*/
function cacheI18n(i18n) {
const config = getConfig()
const files = i18n.files
if(
(config.isServer && config.cacheI18nServer)
|| (!config.isServer && config.cacheI18nClient)
) {
for(let locale in files) {
i18nFilesCache[locale] = i18nFilesCache[locale] || {}
for(let file in files[locale]) {
if(!i18nFilesCache[locale][file]) {
i18nFilesCache[locale][file] = files[locale][file]
}
}
}
}
}
// Context
export const I18nContext = React.createContext()
/**
* Use i18n context
*/
export function useI18n() {
return React.useContext(I18nContext)
}
/**
* With i18n as a props
*/
export function withI18n(ComposedComponent) {
const Component = (props) => <ComposedComponent is={useI18n()} {...props} />
if(ComposedComponent.getInitialProps) {
Component.getInitialProps = ComposedComponent.getInitialProps
}
return Component
}
/**
* Page with i18n as a props
*/
export function pageWithI18n(ComposedComponent, files = DEFAULT_FILES) {
const Component = (props) => {
cacheI18n(props.i18n)
return (
<I18nContext.Provider value={props.i18n}>
<ComposedComponent {...props} />
</I18nContext.Provider>
)
}
Component.getInitialProps = async (ctx) => {
ctx.i18n = await getI18n(ctx.query.locale || getConfig().locale, files)
// Data
let data = {}
// Get component data
if(ComposedComponent.getInitialProps) {
Object.assign(data, await ComposedComponent.getInitialProps(ctx))
}
// Complete
return Object.assign(
data, {
i18n: ctx.i18n,
}
)
}
return Component
}
/**
* Get i18n
* @param {String} locale
* @param {Array} files
*/
export async function getI18n(locale, files = DEFAULT_FILES) {
const i18n = {
locale: locale,
files: {}
}
await Promise.all(files.map((file) => addI18nFile({ file, i18n })))
return i18n
}
/**
* Add a new file to localize
*/
export async function addI18nFile({ file, i18n, locale, path }) {
const config = getConfig()
// Setup
locale = locale || i18n.locale
i18n.files[locale] = i18n.files[locale] || {}
i18nFilesCache[locale] = i18nFilesCache[locale] || {}
// Allready in i18n
if(i18n.files[locale][file]) {
return i18n.files[locale][file]
}
// Allready in cache
if(i18nFilesCache[locale][file]) {
i18n.files[locale][file] = i18nFilesCache[locale][file]
return i18n.files[locale][file]
}
// Load
path = (path || `${config.basePath}${config.assets}locales/{locale}/{file}.json`)
.replace('{locale}', locale)
.replace('{file}', file)
i18n.files[locale][file] = await (await fetch(path)).json()
return i18n.files[locale][file]
}
/**
* Localize
* @param {String} key
* @param {Object} params
* @param {Object} options
*/
export function localize(key, params = null, { file = 'main', locale, defaultValue = null, i18n } = {}) {
i18n = i18n || useI18n()
// i18n not found
if(!i18n) {
return defaultValue
}
locale = locale || i18n.locale
// Seek
let output = (
i18n.files[locale] &&
i18n.files[locale][file]
) ? get(i18n.files[locale][file], key, defaultValue) : defaultValue
// Params
if(output) {
for (var id in params) {
output = output.replace(new RegExp(`\\\{${id}\\\}`, 'g'), params[id]);
}
}
return output
}
/**
* Localize component
* @param {Object} props
*/
export function Localize(props) {
return localize(props.children, props, props)
}
/**
* Gets the value at path of object. If the resolved value is undefined, the defaultValue is returned in its place.
* @param {Object} obj - Targeted object
* @param {String} path - Object path
* @param {*} def - Default value
*/
function get(obj, path, def) {
const fullPath = path
.replace(/\[/g, '.')
.replace(/]/g, '')
.split('.')
.filter(Boolean)
return fullPath.every(everyFunc) ? obj : def
function everyFunc(step) {
return !(step && (obj = obj[step]) === undefined)
}
}