loader.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. const phrasebook = require("./phrasebook.js").phrasebook;
  2. /**
  3. * This class fetch and prepare phrasebook from JSON file. That JSON file
  4. * could be simple flat JSON, which contain string phrases as keys, and
  5. * its translatedequivalents as values. Also avalirable is format, where
  6. * phrases not being used, but JSON contain nested objects. End value must
  7. * being translated string, but then notation like "a.b.c" could being used.
  8. *
  9. * @example simple flat phrasebook ```JSON
  10. * JSON:
  11. * {
  12. * "phrase a": "Translated phrase a",
  13. * "phrase b": "Translated phrase b"
  14. * }
  15. *
  16. * Results:
  17. * "phrase a" -> "Translated phrase a",
  18. * "phrase b" -> "Translated phrase b"
  19. * ```
  20. *
  21. * @example simple nested phrasebook ```JSON
  22. * JSON:
  23. * {
  24. * "phrases": {
  25. * "phrase a": "Translated phrase a"
  26. * },
  27. * "objects": {
  28. * "a": {
  29. * "b": {
  30. * "c": "Second object notation"
  31. * }
  32. * }
  33. * }
  34. * }
  35. *
  36. * Results:
  37. * "phrase a" -> "Translated phrase a",
  38. * "a.b.c" -> "Second object notation"
  39. * ```
  40. */
  41. class loader {
  42. /**
  43. * @var {string}
  44. * This is location of the phrasebook on the server.
  45. */
  46. #path;
  47. /**
  48. * @var {bool}
  49. * This is true, when must load local file, or false when fetch.
  50. */
  51. #local;
  52. /**
  53. * This create new loader of the phrasebook.
  54. *
  55. * @param {string} path - Location of the phrasebook to fetch.
  56. * @param {bool} local - False when must fetch from remote.
  57. */
  58. constructor(path, local = false) {
  59. if (typeof(path) !== "string") {
  60. throw new TypeError("Path of the file must be string.");
  61. }
  62. if (typeof(local) !== "boolean") {
  63. throw new TypeError("Local must be bool variable.");
  64. }
  65. this.#path = path;
  66. this.#local = local;
  67. }
  68. /**
  69. * This load file from path given in the constructor, parse and return it.
  70. *
  71. * @returns {phrasebook} - New phrasebook with content from JSON file.
  72. */
  73. async #load_remote() {
  74. const request = await fetch(this.#path);
  75. const response = await request.json();
  76. return this.#parse(response);
  77. }
  78. /**
  79. * This load file from path given in the constructor, parse and return it.
  80. *
  81. * @returns {phrasebook} - New phrasebook with content from JSON file.
  82. */
  83. async #load_local() {
  84. let fs = null;
  85. NODE: fs = require("node:fs/promises");
  86. if (fs === null) {
  87. throw new Error("Could not use ndoe:fs in browser.");
  88. }
  89. const content = await fs.readFile(this.#path, { encoding: 'utf8' });
  90. const response = JSON.parse(content);
  91. return this.#parse(response);
  92. }
  93. /**
  94. * This load file from path given in the constructor, parse and return it.
  95. *
  96. * @returns {phrasebook} - New phrasebook with content from JSON file.
  97. */
  98. load() {
  99. if (this.#local) {
  100. return this.#load_local();
  101. }
  102. return this.#load_remote();
  103. }
  104. /**
  105. * This parse phrasebook. When phrasebook contain "phrases" or "objects"
  106. * keys, and also "objects" is not string, then parse it as nested file,
  107. * in the other way parse it as flat.
  108. *
  109. * @param {object} content - Fetched object with translations.
  110. * @returns {phrasebook} - Loaded phrasebook.
  111. */
  112. #parse(content) {
  113. const has_objects = (
  114. "objects" in content &&
  115. typeof(content["objects"]) === "object"
  116. );
  117. const has_phrases = (
  118. "phrases" in content &&
  119. typeof(content["phrases"]) === "object"
  120. );
  121. const is_nested = has_objects || has_phrases;
  122. if (is_nested) {
  123. const phrases = has_phrases ? content["phrases"] : {};
  124. const objects = has_objects ? content["objects"] : {};
  125. return new phrasebook(
  126. this.#parse_phrases(phrases),
  127. objects
  128. );
  129. }
  130. return new phrasebook(this.#parse_phrases(content));
  131. }
  132. /**
  133. * This parse flat phrases object to map.
  134. *
  135. * @param {object} content - Flat phrases object to pase.
  136. * @returns {Map} - Phrases parsed as Map.
  137. */
  138. #parse_phrases(content) {
  139. const phrases = new Map();
  140. Object.keys(content).forEach(phrase => {
  141. const name = phrasebook.prepare(phrase);
  142. const translation = content[phrase];
  143. phrases.set(name, translation);
  144. });
  145. return phrases;
  146. }
  147. }
  148. exports.loader = loader;