Source

lib/language_mapper.js

  1. /* This file is part of Ezra Bible App.
  2. Copyright (C) 2019 - 2023 Ezra Bible App Development Team <contact@ezrabibleapp.net>
  3. Ezra Bible App is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. Ezra Bible App is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with Ezra Bible App. See the file LICENSE.
  13. If not, see <http://www.gnu.org/licenses/>. */
  14. /**
  15. * This module contains utility functions to get language name and details by ISO language code or vise versa
  16. * @module languageMapper
  17. * @category Utility
  18. */
  19. var mappingExistsCache = {};
  20. /**
  21. * Function to get language name based on ISO language code. Caches returned values by localeCode
  22. * @param {string} languageCode 2-letter ISO 639-1 or 3-letter ISO 639-2/T or 3-letter language code, might have optional sub-tags for Script and Region that won't be used. See getLanguageDetails
  23. * @param {string} [localeCode='en'] locale/language code to localize language name into
  24. * @returns {(string|undefined)} localized language name or undefined if there is no localization available
  25. */
  26. module.exports.getLanguageName = (languageCode, localeCode='en') => {
  27. if (mappingExistsCache[languageCode] && mappingExistsCache[languageCode][localeCode]) {
  28. return mappingExistsCache[languageCode][localeCode];
  29. }
  30. const details = this.getLanguageDetails(languageCode, localeCode);
  31. const languageName = details.localized || localeCode === 'en' ? details.languageName : undefined;
  32. if (!mappingExistsCache[languageCode]) {
  33. mappingExistsCache[languageCode] = {};
  34. }
  35. mappingExistsCache[languageCode][localeCode] = languageName;
  36. return languageName;
  37. };
  38. /**
  39. * @typedef {Object} LanguageDetails
  40. * @property {string} languageCode Initial normalized 2-letter ISO 639-1 or 3-letter ISO 639-2/T or 3-letter ISO 639-3 language code (without Script or Region sub-tags)
  41. * @property {string} languageName Language name localized or in English
  42. * @property {(string|undefined)} languageScript Script name based on 4-letter ISO-15924 script sub-tag (i.e. Cyrillic, Arabic, Latin...)
  43. * @property {(string|undefined)} languageRegion Region name based on 2-letter ISO 3166-1 region sub-tag (i.e. US, GB...)
  44. * @property {boolean} localized Are languageName, languageScript, languageRegion localized
  45. * @property {string} type ISO 639-3 language type (i.e. 'living', 'historical', 'constructed')
  46. * @property {string} scope ISO 639-3 language scope (i.e. 'individual', 'macrolanguage') See https://en.wikipedia.org/wiki/ISO_639:a
  47. * @property {(string|undefined)} iso6393 ISO 693-3 3-letter language code
  48. * @property {(string|undefined)} iso6392B ISO 693-2/B 3-letter language code
  49. * @property {(string|undefined)} iso6392T ISO 693-2/T 3-letter language code
  50. * @property {(string|undefined)} iso6391 ISO 693-1 2-letter language code
  51. */
  52. /**
  53. * Function to get various language details
  54. * @param {string} languageCode Language identifier is a combination of sub-tags (Language[-Script][-Region]) for Language and optionally Script, and/or Region, according to BCP 47 and RFC 4647
  55. * @param {string} [localeCode='en'] Locale/Language code to localize language information into
  56. * @returns {LanguageDetails} details Language details
  57. */
  58. module.exports.getLanguageDetails = function (languageCode, localeCode='en') {
  59. var [normalizedCode, scriptCode, regionCode] = languageCode.split('-');
  60. if (scriptCode && scriptCode.length < 4) { // if a only a regionCode
  61. regionCode = scriptCode;
  62. scriptCode = undefined;
  63. }
  64. const details = getDetailsByCode(normalizedCode);
  65. if (!details) {
  66. console.log(`Can't find details for the "${languageCode}" (${normalizedCode}) language`);
  67. return {};
  68. }
  69. var languageName;
  70. var localized = false;
  71. if (languageCode === localeCode && regionCode && details.region && details.region[regionCode]) {
  72. languageName = details.regions[regionCode].name;
  73. localized = true;
  74. } else if (details[localeCode]) {
  75. languageName = details[localeCode];
  76. localized = true;
  77. } else {
  78. languageName = details.en && details.name && details.en != details.name ? `${details.en} (${details.name})` : details.en || details.name;
  79. }
  80. languageName = languageName.replace("(individual language)", "").trim(); // this phrase is not useful in the Install Assistant language list
  81. var languageScript;
  82. if (scriptCode) {
  83. const scriptDetails = getDetailsByCode(scriptCode);
  84. languageScript = scriptDetails[localeCode] || scriptDetails["en"];
  85. }
  86. var languageRegion;
  87. if (regionCode) {
  88. if (details.regions && details.regions[regionCode]) {
  89. languageRegion = details.regions[regionCode][localeCode];
  90. } else {
  91. console.log(`Can't map ${normalizedCode}-${regionCode} region`, details);
  92. }
  93. }
  94. return {
  95. ...details,
  96. localized,
  97. languageCode: normalizedCode,
  98. languageName,
  99. languageScript,
  100. languageRegion,
  101. };
  102. };
  103. var langData;
  104. function getDetailsByCode(code) {
  105. if (!langData) {
  106. langData = require('../../lib/languages.json');
  107. }
  108. return langData[code];
  109. }