Skip to content

Commit

Permalink
feat: add the ability to pass a getter for i18n instance
Browse files Browse the repository at this point in the history
  • Loading branch information
felixmosh committed Jul 19, 2022
1 parent e171edc commit 574deab
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 60 deletions.
2 changes: 1 addition & 1 deletion client.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import i18next from 'i18next';

declare const client: {
applyClientHMR(i18n: i18next.i18n): void;
applyClientHMR(i18nOrGetter: i18next.i18n | (() => i18next.i18n)): void;
};

export = client;
42 changes: 23 additions & 19 deletions lib/client-hmr.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,45 @@
const { extractLangAndNS, printList } = require('./utils');
const { extractList, printList, makeUniqueList, createLoggerOnce } = require('./utils');

module.exports = function applyClientHMR(i18n) {
module.exports = function applyClientHMR(i18nOrGetter) {
if (module.hot) {
function log(msg, type = 'log') {
console[type](`[%cI18NextHMR%c] ${msg}`, 'color:#bc93b6', '');
}

const logOnce = createLoggerOnce(log);

const { changedFile } = require('./trigger.js');

if (!changedFile || changedFile) {
// We must use the required variable
log('Client HMR has started');
}

let backendOptions = { queryStringParams: {} };
try {
backendOptions = i18n.options.backend || i18n.services.backendConnector.backend.options;
backendOptions.queryStringParams = backendOptions.queryStringParams || {};
} catch (e) {
log('Client i18next-backend not found, hmr may not work', 'warn');
}
async function reloadTranslations(list, i18nInstance) {
let backendOptions = { queryStringParams: {} };
try {
backendOptions =
i18nInstance.options.backend || i18nInstance.services.backendConnector.backend.options;
backendOptions.queryStringParams = backendOptions.queryStringParams || {};
} catch (e) {
logOnce('Client i18next-http-backend not found, hmr may not work', 'warn');
}

async function reloadTranslations(list) {
backendOptions.queryStringParams._ = new Date().getTime(); // cache killer
const langs = [...new Set(list.map((item) => item.lang))];
const namespaces = [...new Set(list.map((item) => item.ns))];

await i18n.reloadResources(langs, namespaces, (error) => {
const langs = makeUniqueList(list.map((item) => item.lang));
const namespaces = makeUniqueList(list.map((item) => item.ns));

await i18nInstance.reloadResources(langs, namespaces, (error) => {
if (error) {
log(error, 'error');
return;
}

const currentLang = i18n.language;
const currentLang = i18nInstance.language;

if (langs.includes(currentLang)) {
i18n.changeLanguage(currentLang);
i18nInstance.changeLanguage(currentLang);
log(`Update applied successfully`);
} else {
log(`Resources of '${printList(list)}' were reloaded successfully`);
Expand All @@ -45,17 +49,17 @@ module.exports = function applyClientHMR(i18n) {

module.hot.accept('./trigger.js', () => {
const { changedFiles } = require('./trigger.js');
const list = changedFiles
.map((changedFile) => extractLangAndNS(changedFile, i18n.options.ns))
.filter(({ lang, ns }) => Boolean(lang) && Boolean(ns));
const i18nInstance = typeof i18nOrGetter === 'function' ? i18nOrGetter() : i18nOrGetter;

const list = extractList(changedFiles, i18nInstance.options.ns);

if (!list.length) {
return;
}

log(`Got an update with ${printList(list)}`);

return reloadTranslations(list);
return reloadTranslations(list, i18nInstance);
});
}
};
27 changes: 14 additions & 13 deletions lib/server-hmr.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
const { extractLangAndNS, printList } = require('./utils');
const { extractList, printList, makeUniqueList, createLoggerOnce } = require('./utils');

module.exports = function applyServerHMR(i18n) {
module.exports = function applyServerHMR(i18nOrGetter) {
const pluginName = `\x1b[35m\x1b[1m${'I18NextHMR'}\x1b[0m\x1b[39m`;

function log(message) {
console.log(`[ ${pluginName} ] ${message}`);
function log(message, type = 'log') {
console[type](`[ ${pluginName} ] ${message}`);
}

const logOnce = createLoggerOnce(log);

function reloadServerTranslation({ changedFiles }) {
const list = changedFiles
.map((changedFile) => extractLangAndNS(changedFile, i18n.options.ns))
.filter(({ lang, ns }) => Boolean(lang) && Boolean(ns));
const i18nInstance = typeof i18nOrGetter === 'function' ? i18nOrGetter() : i18nOrGetter;

const list = extractList(changedFiles, i18nInstance.options.ns);

if (list.length === 0) {
return;
}

log(`Got an update with ${printList(list)}`);

const langs = [...new Set(list.map((item) => item.lang))];
const namespaces = [...new Set(list.map((item) => item.ns))];
const langs = makeUniqueList(list.map((item) => item.lang));
const namespaces = makeUniqueList(list.map((item) => item.ns));

i18n.reloadResources(langs, namespaces, (error) => {
i18nInstance.reloadResources(langs, namespaces, (error) => {
if (error) {
log(`\x1b[31m\x1b[1m${error}\x1b[0m\x1b[39m`);
} else {
Expand All @@ -35,15 +36,15 @@ module.exports = function applyServerHMR(i18n) {

if (!changedFile || changedFile) {
// We must use the required variable
log(`Server HMR has started`);
logOnce(`Server HMR has started`);
}

module.hot.accept('./trigger.js', () => {
const changedFiles = require('./trigger.js');
reloadServerTranslation(changedFiles);
});
} else {
log(`Server HMR has started`);
logOnce(`Server HMR has started - callback mode`);

const HMRPlugin = require('./plugin');
HMRPlugin.addListener(reloadServerTranslation);
Expand Down
84 changes: 58 additions & 26 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,61 @@
module.exports = {
extractLangAndNS: function (changedFile, currentNSList) {
const changedFileParts = changedFile.replace(/\\/g, '/').split('/');

const firstLongestNSMatchParts = []
.concat(currentNSList)
.map((ns) => ns.split('/'))
.sort((a, b) => b.length - a.length)
.find((optionalNS) =>
optionalNS.every((optionalNSPart) => changedFileParts.includes(optionalNSPart))
);

if (!firstLongestNSMatchParts) {
return { lang: null, ns: null };
}
function extractLangAndNS(changedFile, currentNSList) {
const changedFileParts = changedFile.replace(/\\/g, '/').split('/');

const lang = changedFileParts
.filter((part) => !firstLongestNSMatchParts.includes(part))
.join('/');

return {
lang,
ns: firstLongestNSMatchParts.join('/'),
};
},
printList: function (list) {
return list.map(item => `${item.lang}/${item.ns}`).join(', ')
const firstLongestNSMatchParts = []
.concat(currentNSList)
.map((ns) => ns.split('/'))
.sort((a, b) => b.length - a.length)
.find((optionalNS) =>
optionalNS.every((optionalNSPart) => changedFileParts.includes(optionalNSPart))
);

if (!firstLongestNSMatchParts) {
return { lang: null, ns: null };
}

const lang = changedFileParts
.filter((part) => !firstLongestNSMatchParts.includes(part))
.join('/');

return {
lang,
ns: firstLongestNSMatchParts.join('/'),
};
}

function printList(list) {
return list.map((item) => `${item.lang}/${item.ns}`).join(', ');
}

function extractList(changedFiles, currentNSList) {
return changedFiles
.map((changedFile) => extractLangAndNS(changedFile, currentNSList))
.filter(({ lang, ns }) => Boolean(lang) && Boolean(ns));
}

function makeUniqueList(list) {
return [...new Set(list)];
}

function createLoggerOnce(log) {
const msgCount = new Map();
return (msg, type = 'log') => {
if (!msgCount.has(msg)) {
msgCount.set(msg, 0);
}

if (msgCount.get(msg) >= 1) {
return;
}

log(msg, type);
msgCount.set(msg, 1);
};
}

module.exports = {
printList: printList,
extractList: extractList,
makeUniqueList: makeUniqueList,
createLoggerOnce: createLoggerOnce,
};
2 changes: 1 addition & 1 deletion server.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import i18next from 'i18next';

declare const server: {
applyServerHMR(i18n: i18next.i18n): void;
applyServerHMR(i18nOrGetter: i18next.i18n | (() => i18next.i18n)): void;
};

export = server;

0 comments on commit 574deab

Please sign in to comment.