How to use i18n JSON translations from some JS scripts that are to be ran only client-side, from a Node.js / Express / EJS website?

huangapple go评论79阅读模式
英文:

How to use i18n JSON translations from some JS scripts that are to be ran only client-side, from a Node.js / Express / EJS website?

问题

I18n 用于管理 Node.js、Express 和 EJS 构建的网站的所有内容的翻译。它在服务器端使用 JSON 文件。已经更新了除一个页面之外的所有所需页面。客户端习惯于在每个页面的页脚调用一个名为 js 文件的文件,该文件非常密集,仅在客户端运行。

由于时间限制,我们希望不必重构此代码。

我如何能够在仅在客户端运行的脚本中使用服务器端的 JSON 翻译文件呢?

我考虑过创建一个专用的 API 服务,供客户端调用并获取所有翻译,但这似乎相当繁重。

或者我考虑使用 Node.js,在客户端运行的 js 文件末尾手动编写翻译的 JSON 文件,以便在它们更新时生成。但这将意味着成千上万行代码,诚然,在这种情况下,我必须忘记编写完美干净的代码,但是...

在继续之前,我真的很感激您的帮助。我本来想进行重构,但出于某些棘手的原因,现在可能不是最好的时机。

谢谢大家,祝你们一天愉快。 How to use i18n JSON translations from some JS scripts that are to be ran only client-side, from a Node.js / Express / EJS website?

英文:

Here is my problem,

I am using i18n to manage the translations of all the content of a website made on Node.js, Express and EJS. i18n uses json files server-side. I've updated all pages I needed to, except for one.

My client is used to have a js file called from the footer on every pages. This file is pretty dense, and is ran only client-side.

Because of time constraints, we would prefer not to refactor this code.

How could I use my json translation files server-side from a script that is to be run only client side ?

I though about making a dedicated api service to call from client side and get all the translations but it seems pretty heavy.

Or I though about using Node.js to write manually, at the end of my js file ran client side, the translations JSON files when they'd get updated. But that would mean thousands of lines, it's true I have to forget writing perfect clean code for this case, but, well...

Before going further I'd really appreciate your help on this. I would have love just to refactor, but for some tricky reasons, it may not be the greatest for now.

Thanks and have a great day you all. How to use i18n JSON translations from some JS scripts that are to be ran only client-side, from a Node.js / Express / EJS website?

答案1

得分: 0

以下是翻译好的内容:

What I did finally:
最终我做了什么:

I've created a directory in /assets to store all translation json files.
我创建了一个目录在/assets下来存储所有的翻译JSON文件。

If on test or dev environment, at every request I check if those files have been updated before or after /locales json files.
如果在测试或开发环境中,每次请求时,我都会检查这些文件是否在/locales JSON文件之前或之后被更新。

/server.js
/server.js

const { watchAssetsTrads } = require("./middleware/clientSideTrads");
const notInProd = env === "test" || env === "dev";
if (notInProd) app.use((req, res, next) => watchAssetsTrads(next));

我需要改进这个next文件,因为我在这里学到了如何管理我的承诺不好,尽管它运行得很好。
我需要改进这个next文件,因为我在这里学到了如何管理我的承诺不好,尽管它运行得很好。

/middlewares/clientSideTrads.js
/middlewares/clientSideTrads.js

const debug = require("debug")("trads");
const fs = require('fs');
const lodash = require('lodash');
const path = require('path');

const locales = path.join(__dirname, '../locales/');
const assetsTrads = path.join(__dirname, '../assets/trads/');

const getFilesNamesFrom = path => {
  return new Promise((resolve, reject) => {
    fs.readdir(path, (err, files) => {
      if (err) return reject(err);
      return resolve(files.filter(e => e !== ".DS_Store"));
    });
  });
};

const read = jsonPath => {
  return new Promise((resolve, reject) => {
    fs.readFile(jsonPath, (err, data) => {
      if (err) return reject(err);
      return resolve(JSON.parse(data));
    });
  });
};

const update = (jsonPath, newJson) => {
  return new Promise((resolve, reject) => {
    fs.writeFile(jsonPath, JSON.stringify(newJson), err => {
      if (err) return reject(err);
      return resolve();
    });
  });
};

const getLastUpdateTime = filePath => {
  return new Promise((resolve, reject) => {
    fs.stat(filePath, (err, stats) => {
      if (err) return reject(err);
      return resolve(stats.mtime);
    });
  });
};

const updateAssetsTrads = async (localesTradPath, assetsTradPath) => {
  const localesTrad = await read(localesTradPath);
  const selectedKeys = lodash.pickBy(localesTrad, function(value, key) {
    if ((/^clientside/u).test(key)) return true;
    return false;
  });
  await update(assetsTradPath, selectedKeys);
  return null;
};

const watchAssetsTrads = async next => {
  debug('Checking asset trads');
  const files = await getFilesNamesFrom(locales);
  files.forEach(async file => {
    try {
      const localesTradPath = path.join(locales, file);
      const assetsTradPath = path.join(assetsTrads, file);
      const lastUpdate = await getLastUpdateTime(localesTradPath);
      const lastMerge = await getLastUpdateTime(assetsTradPath);
      if (lastUpdate > lastMerge) {
        updateAssetsTrads(localesTradPath, assetsTradPath);
        debug('File updated');
      }
    }
    catch (err) {
      return debug(err);
    }
    return null;
  });
  return next();
};

module.exports = {
  watchAssetsTrads
};

With i18n I manage translations via a cookie. As I mentioned, I needed to use a script file included in all pages on footer. There, I've imported this:
使用i18n,我通过一个cookie来管理翻译。正如我提到的,我需要在所有页面的页脚中包含一个脚本文件。在那里,我导入了这个:

getCurrentLanguage.js
getCurrentLanguage.js

const getCookie = () => {
  const cookies = document.cookie.split('; ');
  const myCookie = cookies.filter(c => (/^my-cookie/u).test(c));
  if (typeof myCookie[0] == 'undefined') return 'fr';
  const cookieSplitted = myCookie[0].split('=');
  const currentLanguage = cookieSplitted[1];
  return currentLanguage;
};

const getCurrentLanguage = () => {
  const currentLanguage = getCookie();
  return currentLanguage;
};

export { getCurrentLanguage };

And then, I get all translations and return the one available for the current language client-side (I realize I only need to get one translation file, improve in coming).
然后,我获取所有的翻译并返回客户端当前语言可用的翻译(我意识到我只需要获取一个翻译文件,在未来改进)。

getTrads.js
getTrads.js

const createTradsObjFrom = async (languages, tradsPaths) => {
  try {
    const obj = {};
    for (const [index, lang] of languages.entries()) {
      const path = tradsPaths[index];
      obj[lang] = {
        json: await $.getJSON(path)
      };
    }
    return obj;
  }
  catch (err) {
    throw new Error("some error");
  }
};

const getTrads = currentLanguage => {
  return new Promise((resolve, reject) => {
    const tradsDir = "/assets/trads/";
    const languages = [
      "de",
      "en",
      "es",
      "fr",
      "it",
      "nl",
      "ru"
    ];
    const tradsPaths = languages.map(e => tradsDir + e + '.json');
    createTradsObjFrom(languages, tradsPaths)
    .then(trads => resolve(trads[currentLanguage].json))
    .catch(err => reject(err));
  });
};

export { getTrads };

希望这些翻译对您有所帮助。如果您有任何其他问题,请随时提出。

英文:

What I did finally:

I've created a directory in /assets to store all translation json files.

If on test or dev environment, at every request I check if those files have been updated before or after /locales json files.

> /server.js

const { watchAssetsTrads } = require("./middleware/clientSideTrads");
const notInProd = env === "test" || env === "dev";
if (notInProd) app.use((req, res, next) => watchAssetsTrads(next));

I need to improve this next file as I've learned here how I managed poorly my promises, though it works great.

> /middlewares/clientSideTrads.js

const debug = require("debug")("trads");
const fs = require('fs');
const lodash = require('lodash');
const path = require('path');
const locales = path.join(__dirname, '../locales/');
const assetsTrads = path.join(__dirname, '../assets/trads');
const getFilesNamesFrom = path => {
return new Promise((resolve, reject) => {
fs.readdir(path, (err, files) => {
if (err) return reject(err);
return resolve(files.filter(e => e !== ".DS_Store"));
});
});
};
const read = jsonPath => {
return new Promise((resolve, reject) => {
fs.readFile(jsonPath, (err, data) => {
if (err) return reject(err);
return resolve(JSON.parse(data));
});
});
};
const update = (jsonPath, newJson) => {
return new Promise((resolve, reject) => {
fs.writeFile(jsonPath, JSON.stringify(newJson), err => {
if (err) return reject(err);
return resolve();
});
});
};
const getLastUpdateTime = filePath => {
return new Promise((resolve, reject) => {
fs.stat(filePath, (err, stats) => {
if (err) return reject(err);
return resolve(stats.mtime);
});
});
};
const updateAssetsTrads = async (localesTradPath, assetsTradPath) => {
const localesTrad = await read(localesTradPath);
const selectedKeys = lodash.pickBy(localesTrad, function(value, key) {
if ((/^clientside/u).test(key)) return true;
return false;
});
await update(assetsTradPath, selectedKeys);
return null;
};
const watchAssetsTrads = async next => {
debug('Checking asset trads');
const files = await getFilesNamesFrom(locales);
files.forEach(async file => {
try {
const localesTradPath = path.join(locales, file);
const assetsTradPath = path.join(assetsTrads, file);
const lastUpdate = await getLastUpdateTime(localesTradPath);
const lastMerge = await getLastUpdateTime(assetsTradPath);
if (lastUpdate > lastMerge) {
updateAssetsTrads(localesTradPath, assetsTradPath);
debug('File updated');
}
}
catch (err) {
return debug(err);
}
return null;
});
return next();
};
module.exports = {
watchAssetsTrads
};

With i18n I manage translations via a cookie. As I mentioned, I needed to use a script file included in all pages on footer. There, I've imported this:

> getCurrentLanguage.js

const getCookie = () => {
const cookies = document.cookie.split('; ');
const myCookie = cookies.filter(c => (/^my-cookie/u).test(c));
if (typeof myCookie[0] == 'undefined') return 'fr';
const cookieSplitted = myCookie[0].split('=');
const currentLanguage = cookieSplitted[1];
return currentLanguage;
};
const getCurrentLanguage = () => {
const currentLanguage = getCookie();
return currentLanguage;
};
export { getCurrentLanguage };

And then, I get all translations and return the one available for current language client side (I realize I only need to get one translation file, improve in coming).

> getTrads.js

const createTradsObjFrom = async (languages, tradsPaths) => {
try {
const obj = {};
for (const [index, lang] of languages.entries()) {
const path = tradsPaths[index];
obj[lang] = {
json: await $.getJSON(path)
};
}
return obj;
}
catch (err) {
throw new Error("some error");
}
};
const getTrads = currentLanguage => {
return new Promise((resolve, reject) => {
const tradsDir = "/assets/trads/";
const languages = [
"de",
"en",
"es",
"fr",
"it",
"nl",
"ru"
];
const tradsPaths = languages.map(e => tradsDir + e + '.json');
createTradsObjFrom(languages, tradsPaths)
.then(trads => resolve(trads[currentLanguage].json))
.catch(err => reject(err));
});
};
export { getTrads };

huangapple
  • 本文由 发表于 2020年1月6日 17:43:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/59609785.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定