tap.js 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import { ReactiveCache } from '/imports/reactiveCache';
  2. import { Meteor } from 'meteor/meteor';
  3. import { ReactiveVar } from 'meteor/reactive-var';
  4. import Translation from '/models/translation';
  5. import i18next from 'i18next';
  6. import sprintf from 'i18next-sprintf-postprocessor';
  7. import languages from './languages';
  8. const DEFAULT_NAMESPACE = 'translation';
  9. const DEFAULT_LANGUAGE = 'en';
  10. // Carefully reproduced tap:i18n API
  11. export const TAPi18n = {
  12. i18n: null,
  13. current: new ReactiveVar(DEFAULT_LANGUAGE),
  14. async init() {
  15. this.i18n = i18next.createInstance().use(sprintf);
  16. await this.i18n.init({
  17. fallbackLng: DEFAULT_LANGUAGE,
  18. cleanCode: true,
  19. // Show translations debug messages only when DEBUG=true
  20. // OLD: debug: Meteor.isDevelopment,
  21. debug: process.env.DEBUG,
  22. supportedLngs: Object.values(languages).map(({ tag }) => tag),
  23. ns: DEFAULT_NAMESPACE,
  24. defaultNs: DEFAULT_NAMESPACE,
  25. postProcess: ["sprintf"],
  26. // Default values to match tap:i18n behaviour
  27. interpolation: {
  28. prefix: '__',
  29. suffix: '__',
  30. escapeValue: false,
  31. },
  32. resources: {},
  33. });
  34. // Load the current language data
  35. await TAPi18n.loadLanguage(DEFAULT_LANGUAGE);
  36. },
  37. isLanguageSupported(language) {
  38. return Object.values(languages).some(({ tag }) => tag === language);
  39. },
  40. getSupportedLanguages() {
  41. return Object.values(languages).map(({ name, code, tag }) => ({ name, code, tag }));
  42. },
  43. getLanguage() {
  44. return this.current.get();
  45. },
  46. loadTranslation(language) {
  47. return new Promise((resolve, reject) => {
  48. if (Meteor.isClient) {
  49. const translationSubscription = Meteor.subscribe('translation', {language: language}, 0, {
  50. onReady() {
  51. resolve(translationSubscription);
  52. },
  53. onError(error) {
  54. reject(error);
  55. }
  56. });
  57. } else {
  58. resolve();
  59. }
  60. });
  61. },
  62. async loadLanguage(language) {
  63. if (language in languages && 'load' in languages[language]) {
  64. let data = await languages[language].load();
  65. let custom_translations = [];
  66. await this.loadTranslation(language);
  67. custom_translations = ReactiveCache.getTranslations({language: language}, {fields: { text: true, translationText: true }});
  68. if (custom_translations && custom_translations.length > 0) {
  69. data = custom_translations.reduce((acc, cur) => (acc[cur.text]=cur.translationText, acc), data);
  70. }
  71. this.i18n.addResourceBundle(language, DEFAULT_NAMESPACE, data);
  72. } else {
  73. throw new Error(`Language ${language} is not supported`);
  74. }
  75. },
  76. async setLanguage(language) {
  77. await this.loadLanguage(language);
  78. await this.i18n.changeLanguage(language);
  79. this.current.set(language);
  80. },
  81. // Return translation by key
  82. __(key, options, language) {
  83. this.current.dep.depend();
  84. return this.i18n.t(key, {
  85. ...options,
  86. lng: language,
  87. });
  88. }
  89. };