| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 | import React, { useContext, useState } from 'react';import PropTypes from 'prop-types';const DEFAULT_LANGUAGE = 'en';const LANGUAGES_PATH = 'static/locales';const LANGUAGES_FILE = 'translations.json';const VARIABLE_REGEX = /\{([^}]+)\}/g;export const store = {    /* dictionaries */};/** * Set a dictionary for a specific language * @param  {String} lang language * @param  {Object} data dictionary * @return {Object}      dictionary added */export const setDict = (lang, data) => (store[lang] = data);/** * Get a dictionary for specific language * @param  {String} lang language * @return {Object}      dictionary */export const getDict = lang => store[lang];/** * Fetch a dictionary for specific language defined in JSON format * @param  {Strong} lang language * @return {Object}      dictionary loaded */export const fetchStore = async lang => {    if (!store[lang]) {        const res = await fetch(`/${LANGUAGES_PATH}/${lang}/${LANGUAGES_FILE}`);        store[lang] = await res.json();    }    return store[lang];};/** * Interpolates values given in 'params' * @param  {String} str    text * @param  {Object} params object to interpolate * @return {String} */const compile = (str = '', params) => {    const matches = str.match(VARIABLE_REGEX);    if (matches) {        matches            .map(v => v.replace(/\{|\}/g, ''))            .forEach(v => (str = str.replace('{' + v + '}', params[v])));    }    return str;};/** * Reads an object value using dot notation for nested keys * @param  {Object} obj  object to parse * @param  {String} skey key to search * @return {String|null} */export const accessKey = (obj, skey = '') => skey.split('.').reduce((a, b) => a && a[b], obj);/** * Returns the translated text for given key and interpolates values in 'params' * @param  {String} key    the key to search for * @param  {String} lang   language * @param  {Object} params object with params to interpolate * @return {[type]}        [description] */export const translateKey = (key, lang, params = {}) => compile(accessKey(getDict(lang), key));// =====================// REACT INTERFACE// =====================/** * Context used to handle translation in the component tree */const TranslateContext = React.createContext({    language: DEFAULT_LANGUAGE,    changeLanguage: () => {}});/** * Component provider to pass down context to child components * Must be used to wrap application */export class Provider extends React.Component {    changeLanguage = language => {        fetchStore(language).then(() => {            this.setState(state => ({                language            }));        });    };    state = {        language: DEFAULT_LANGUAGE,        changeLanguage: this.changeLanguage    };    constructor(props) {        super(props);        if (props.store) {            Object.keys(props.store).forEach(l => setDict(l, props.store[l]));        }    }    render() {        return (            <TranslateContext.Provider value={this.state}>                {this.props.children}            </TranslateContext.Provider>        );    }}/** * HOC to provide 'changeLanguage' and 't' method to WrappedComponent */export function withTranslation(WrappedComponent) {    return function TranslatedComponent(props) {        const { language, changeLanguage } = useContext(TranslateContext);        const t = (k, l, p) => translateKey(k, l || language, p);        return <WrappedComponent changeLanguage={changeLanguage} t={t} {...props} />;    };}/** * Component to translate a given key * If key is missing, render children without changes */export const Trans = props => {    const { language } = useContext(TranslateContext);    const { i18nKey, lang, params, children } = props;    return [translateKey(i18nKey, lang || language, params) || children];};Trans.proptypes = {    i18nKey: PropTypes.string.isRequired,    lang: PropTypes.string,    params: PropTypes.object};
 |