languages.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. const loader = require("./loader.js").loader;
  2. const phrasebook = require("./phrasebook.js").phrasebook;
  3. /**
  4. * This class represents languages library. This store its location on the
  5. * server, and create loaders for them. It could also load langiage
  6. * library from json file.
  7. */
  8. class languages {
  9. /**
  10. * @var {string}
  11. * This represents path to directory where phrasebooks had been stored.
  12. */
  13. #path;
  14. /**
  15. * @var {Map}
  16. * This store languages and its files on server.
  17. */
  18. #libs;
  19. /**
  20. * This create new languages library. Next, languages could be added to
  21. * the library by command, or by loading index file.
  22. *
  23. * @throws {TypeError} - When parameters is not in correct format.
  24. *
  25. * @param {string} path - Path to phrasebooks on the server or filesystem.
  26. */
  27. constructor(path) {
  28. if (typeof(path) !== "string") {
  29. throw new TypeError("Path to the phrasebooks must be string.");
  30. }
  31. this.#path = path;
  32. this.#libs = new Map();
  33. }
  34. /**
  35. * This add new language to the library by name. Name must be in form
  36. * like POSIX locale, like en_US, or pl_PL. That mean first two letter
  37. * mest be ISO 639-1 and second two letters mst be in ISO 3166-1 alpha-2
  38. * 2 letter country code format.
  39. *
  40. * @see https://www.loc.gov/standards/iso639-2/php/code_list.php
  41. * @see https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes
  42. * @see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
  43. * @see https://en.wikipedia.org/wiki/Locale_(computer_software)
  44. *
  45. * @throws {TypeError} - When tpes of the parameters is not correct.
  46. *
  47. * @param {string} name - Name of the language, like "en_US".
  48. * @param {string} file - Name of the file in the directory.
  49. * @return {languages} - Instnace of this class to chain.
  50. */
  51. add(name, file) {
  52. if (typeof(name) !== "string") {
  53. throw new TypeError("Name of the language must be sting.");
  54. }
  55. if (typeof(file) !== "string") {
  56. throw new TypeError("File on in the directory must be string.");
  57. }
  58. if (this.#libs.has(name)) {
  59. console.error("Language \"" + name + "\" already loaded.");
  60. console.error("It could not being loaded twice.");
  61. return this;
  62. }
  63. if (!this.#valid_locale(name)) {
  64. console.error("Language name \"" + name + "\" invalid formated.");
  65. console.error("It could not being loaded.")
  66. return this;
  67. }
  68. this.#libs.set(name, file);
  69. return this;
  70. }
  71. /**
  72. * This load all phrasebook given in the index file. Index must be
  73. * JSON file, which contain one object. That object properties must be
  74. * languages names in the notation like in add function. Valus of that
  75. * properties musts being strings which contains names of the phrasebook
  76. * files in the path directory.
  77. *
  78. * @example ``` { "pl_PL": "polish.json", "en_US": "english.json" } ```
  79. *
  80. * @see add
  81. *
  82. * @param {string} index - Index file in the phrasebook directory.
  83. * @return {languages} - New languages instance with loaded index.
  84. */
  85. async load(index) {
  86. if (typeof(index) !== "string") {
  87. throw new TypeError("Name of index file is not string.");
  88. }
  89. const request = await fetch(this.#full_path(index));
  90. const response = await request.json();
  91. const result = new languages(this.#path);
  92. Object.keys(response).forEach(name => {
  93. if (typeof(name) !== "string") {
  94. console.error("Name of the language must be string.");
  95. console.error("Check languages index.");
  96. console.error("Skipping it.")
  97. return;
  98. }
  99. if (typeof(response[name]) !== "string") {
  100. console.error("Name of phrasebook file must be string.");
  101. console.error("Check languages index.");
  102. console.error("Skipping it.");
  103. }
  104. result.add(name, response[name]);
  105. });
  106. return result;
  107. }
  108. /**
  109. * This check that language exists in languages library.
  110. *
  111. * @param {string} name - Name of the language to check.
  112. * @return {bool} - True when language exists, false when not
  113. */
  114. has(name) {
  115. return this.#libs.has(name);
  116. }
  117. /**
  118. * This return all avairable languages.
  119. *
  120. * @return {Array} - List of all avairable languages.
  121. */
  122. get avairable() {
  123. const alls = new Array();
  124. this.#libs.keys().forEach(name => {
  125. alls.push(name);
  126. });
  127. return alls;
  128. }
  129. /**
  130. * This load phrasebook with give name.
  131. *
  132. * @throws {TypeError} - Param type is not correct.
  133. * @throws {RangeError} - Language not exists in libs.
  134. *
  135. * @param {string} name - Name of the language to load.
  136. * @returns {phrasebook} - Phrasebook loaded from the file.
  137. */
  138. select(name) {
  139. if (typeof(name) !== "string") {
  140. throw new TypeError("Name of the language must be string.");
  141. }
  142. if (!this.has(name)) {
  143. throw new RangeError("Not found language \"" + name + "\".");
  144. }
  145. const file = this.#libs.get(name);
  146. const path = this.#full_path(file);
  147. return new loader(path).load();
  148. }
  149. /**
  150. * This return full path to the file.
  151. *
  152. * @param {string} name - Name of the file to get its path
  153. * @return {string} - Full path of the file
  154. */
  155. #full_path(name) {
  156. let glue = "/";
  157. if (this.#path[this.#path.length - 1] === glue) {
  158. glue = "";
  159. }
  160. return this.#path + glue + name;
  161. }
  162. /**
  163. * This check that format is valid POSIX like locale.
  164. *
  165. * @param {string} name - Name to check format of.
  166. * @return {bool} - True when format is valid, false when not.
  167. */
  168. #valid_locale(name) {
  169. const splited = name.split("_");
  170. if (splited.length !== 2) {
  171. return false;
  172. }
  173. const first = splited[0];
  174. const second = splited[1];
  175. if (first.toLowerCase() !== first || first.length !== 2) {
  176. return false;
  177. }
  178. if (second.toUpperCase() !== second || second.length !== 2) {
  179. return false;
  180. }
  181. return true;
  182. }
  183. }
  184. exports.languages = languages;