{"version":3,"file":"index.mjs","sources":[""],"sourcesContent":["import { bubble } from \"@web3r/flowerkit/evt\";\r\nimport { getMergedObj } from \"@web3r/flowerkit/obj\";\r\nimport i18next from \"i18next\";\r\nimport iso from \"iso-3166-1\";\r\nimport { initReactI18next } from \"react-i18next\";\r\nimport { getDocument } from \"ssr-window\";\r\nimport type {\r\n  TLocaleFn,\r\n  TLocalisationCfg,\r\n  TLocalisationCfgInput,\r\n  TLocalisationOnReady,\r\n  TLocalisationResourceAddedEvent,\r\n} from \"./types\";\r\nimport * as localisationUtils from \"./utils\";\r\nimport type { TLocaleSource } from \"./utils\";\r\nimport { constants } from \"../../constants\";\r\n\r\n/**\r\n * Singleton. Плагин локализации.\r\n * Использует библиотеку `i18next`. Локаль по умолчанию указывается в атрибуте `lang` корневого элемента документа.\r\n * @module Localisation\r\n */\r\nexport class Localisation {\r\n\r\n  cfg: TLocalisationCfg<Localisation>;\r\n\r\n  lib: typeof i18next;\r\n\r\n  /**\r\n   * Набор утилитарных функций\r\n   * @type {typeof localisationUtils}\r\n   */\r\n  static utils = localisationUtils;\r\n\r\n  /**\r\n   * Singleton-экземпляр плагина локализации.\r\n   * @type {Localisation|null}\r\n   */\r\n  static instance: Localisation | null = null;\r\n\r\n  static #loadingPromise: Promise<Localisation> | null = null;\r\n\r\n  static #pendingInstance: Localisation | null = null;\r\n\r\n  /**\r\n   * Признак выполнения кода в SSR-окружении (без объекта window).\r\n   * @type {boolean}\r\n   */\r\n  static isSSR = typeof window === \"undefined\";\r\n\r\n  /**\r\n   * Признак регистрации плагина в глобальном namespace Core.\r\n   * @type {boolean}\r\n   */\r\n  static isApp = true;\r\n\r\n  /**\r\n   * Состояния жизненного цикла инициализации локализации.\r\n   * @type {{isReady: boolean, isError: boolean, isLoading: boolean}}\r\n   */\r\n  static states = {\r\n    isReady: false,\r\n    isError: false,\r\n    isLoading: false,\r\n  };\r\n\r\n  /**\r\n   * Признак готовности плагина локализации.\r\n   * @type {boolean}\r\n   */\r\n  static get isReady() {\r\n    return Localisation.states.isReady;\r\n  }\r\n\r\n  /**\r\n   * Признак выполнения инициализации.\r\n   * @type {boolean}\r\n   */\r\n  static get isLoading() {\r\n    return Localisation.states.isLoading;\r\n  }\r\n\r\n  /**\r\n   * Признак ошибки инициализации.\r\n   * @type {boolean}\r\n   */\r\n  static get isError() {\r\n    return Localisation.states.isError;\r\n  }\r\n\r\n  /**\r\n   * Конфигурация Localisation по умолчанию.\r\n   * @type {TLocalisationCfg<Localisation>}\r\n   */\r\n  static defaultCfg: TLocalisationCfg<Localisation> = {\r\n    locale: \"\",\r\n    langKeys: constants.defaultLangKeys,\r\n    locales: {},\r\n    isDebug: false,\r\n    fallbackNS: \"translation\",\r\n    load: \"LanguageOnly\",\r\n    onMiss: null,\r\n    onChange: null,\r\n    onReady: null,\r\n    onError: null,\r\n  };\r\n\r\n  static #getErrorMsg = (type = \"\") => {\r\n    const prefix = `[${Localisation.constructor.name}] `;\r\n    const getErrBody = () => {\r\n      switch (type) {\r\n        case \"custom\":\r\n          return \"\";\r\n        case \"loading\":\r\n          return \"i18next is already loading\";\r\n        case \"ns\":\r\n          return \"Param `ns` are not defined\";\r\n        case \"lang\":\r\n          return \"Param `lang` are not defined\";\r\n        case \"code\":\r\n          return \"Code not found in current locales\";\r\n        case \"bundle\":\r\n          return \"Resource bundle already exist\";\r\n        default:\r\n          return \"Unexpected error\";\r\n      }\r\n    };\r\n    return prefix + getErrBody();\r\n  };\r\n\r\n  // Хранилище колбэков готовности с явным указанием дженерика\r\n  static #onReadyCallbacks = new Set<TLocalisationOnReady<Localisation>>();\r\n\r\n  // Хранилище колбэков добавления ресурсов\r\n  static #resourceListeners = new Set<(data: TLocalisationResourceAddedEvent) => void>();\r\n\r\n  /**\r\n   * Создаёт или возвращает существующий экземпляр плагина локализации.\r\n   * @param cfg{Object=} пользовательская конфигурация\r\n   * @returns {Promise<Localisation>} промис с экземпляром плагина\r\n   */\r\n  static create(cfg: TLocalisationCfgInput<Localisation> = {}): Promise<Localisation> {\r\n    if (Localisation.instance) {\r\n      return Promise.resolve(Localisation.instance);\r\n    }\r\n\r\n    if (Localisation.#loadingPromise) {\r\n      return Localisation.#loadingPromise;\r\n    }\r\n\r\n    // Конструктор устанавливает #loadingPromise, возвращаем его\r\n    new Localisation(cfg);\r\n    return Localisation.#loadingPromise!;\r\n  }\r\n\r\n  /**\r\n   * Не используйте конструктор напрямую. Воспользуйтесь статическим методом `Localisation.create()`.\r\n   * @param cfg{Object=} пользовательская конфигурация\r\n   * @throws {Error} при попытке повторного создания экземпляра\r\n   */\r\n  constructor(cfg: TLocalisationCfgInput<Localisation> = {}) {\r\n\r\n    if (Localisation.instance || Localisation.#pendingInstance) {\r\n      throw new Error(\r\n        `[Localisation] Use Localisation.create() instead of new Localisation()`\r\n      );\r\n    }\r\n\r\n    this.cfg = this.#getCfg(cfg);\r\n    this.lib = i18next;\r\n    Localisation.#pendingInstance = this;\r\n\r\n    if (typeof this.cfg.onReady === \"function\") {\r\n      Localisation.addReadyCallback(this.cfg.onReady);\r\n    }\r\n\r\n    // установка языка\r\n    if (typeof this.cfg.locale === \"string\" && this.cfg.locale.length) {\r\n      const split = this.cfg.locale.split(\"-\");\r\n      if (split.length > 1) {\r\n        console.error(`[Localisation] Wrong value for locale, use short format instead: \"ru-RU\" => \"ru\"`);\r\n      }\r\n      this.cfg.locale = Localisation.#getResolvedLang(this.cfg.locale);\r\n    } else {\r\n      this.cfg.locale = this.#getUserLanguage();\r\n    }\r\n\r\n    Localisation.#loadingPromise = this.#init()\r\n      .then(() => {\r\n        Localisation.instance = this;\r\n        if (!Localisation.isSSR) {\r\n          console.debug(`[Localisation] Initialized with lang \"${i18next.language}\" (supported: \"${i18next?.languages?.join(\", \")}\")`);\r\n        }\r\n        return this;\r\n      })\r\n      .catch((err) => {\r\n        this.#onError(err);\r\n        return this;\r\n      })\r\n      .finally(() => {\r\n        Localisation.states.isLoading = false;\r\n        Localisation.#pendingInstance = null;\r\n        Localisation.#loadingPromise = null;\r\n      });\r\n  }\r\n\r\n  /**\r\n   * Получает объединённую конфигурацию плагина\r\n   * @param cfg{Object=} пользовательская конфигурация\r\n   * @returns {Object} итоговая конфигурация\r\n   */\r\n  #getCfg(cfg: TLocalisationCfgInput<Localisation> = {}): TLocalisationCfg<Localisation> {\r\n    return {\r\n      ...Localisation.defaultCfg,\r\n      ...cfg,\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Регистрирует callback, вызываемый при готовности плагина локализации.\r\n   * Если плагин уже готов, callback будет вызван немедленно.\r\n   * @param callback{Function} callback-функция\r\n   */\r\n  static addReadyCallback(callback: TLocalisationOnReady<Localisation>): void {\r\n    if (typeof callback !== \"function\") {\r\n      console.error(`[Localisation] Callback must be function, but got \"${typeof callback}\"`);\r\n      return;\r\n    }\r\n\r\n    // Если локализация уже готова\r\n    if (Localisation.instance && Localisation.isReady) {\r\n      callback(Localisation.instance);\r\n      return;\r\n    }\r\n\r\n    // Иначе сохраняем для будущего вызова\r\n    if (!Localisation.#onReadyCallbacks.has(callback)) {\r\n      Localisation.#onReadyCallbacks.add(callback);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Устанавливает callback, вызываемый при готовности плагина локализации.\r\n   * @param callback{Function|null|undefined} callback-функция\r\n   * @deprecated Используйте статический метод `Localisation.addReadyCallback()`\r\n   */\r\n  static set onReady(callback: TLocalisationOnReady<Localisation> | null | undefined) {\r\n    if (callback === null || typeof callback === \"undefined\") {\r\n      return;\r\n    }\r\n    Localisation.addReadyCallback(callback);\r\n  }\r\n\r\n  /**\r\n   * Подписка на добавление ресурсов локализации.\r\n   * Возвращает функцию для отписки.\r\n   * @param listener{Function} обработчик события добавления ресурсов\r\n   * @returns {Function} функция для отписки\r\n   */\r\n  static onResourceAdded(listener: (data: TLocalisationResourceAddedEvent) => void): () => void {\r\n    Localisation.#resourceListeners.add(listener);\r\n    return () => {\r\n      Localisation.#resourceListeners.delete(listener);\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Уведомляет всех подписчиков о добавлении ресурсов\r\n   */\r\n  static #notifyResourceAdded(data: Omit<TLocalisationResourceAddedEvent, \"hasCode\">): void {\r\n    const eventData: TLocalisationResourceAddedEvent = {\r\n      isSilent: !Localisation.isSSR,\r\n      ...data,\r\n      isHasCode: (code: string) => {\r\n        return data.keys.some((key) => {\r\n          // Точное совпадение\r\n          if (key === code) {\r\n            return true;\r\n          }\r\n          // Код может быть вложенным ключом (например: code = \"forms.invalidInput\", key = \"forms\")\r\n          if (code.startsWith(key + \".\")) {\r\n            return true;\r\n          }\r\n          // Ключ может быть родительским для code (например: code = \"forms\", key = \"forms.invalidInput\")\r\n          if (key.startsWith(code + \".\")) {\r\n            return true;\r\n          }\r\n          return false;\r\n        });\r\n      },\r\n    };\r\n\r\n    if (!eventData.isSilent) {\r\n      console.debug(`[Localisation] Resources added:`, eventData);\r\n    }\r\n\r\n    Localisation.#resourceListeners.forEach((listener) => {\r\n      try {\r\n        listener(eventData);\r\n      } catch (err) {\r\n        console.error(\"[Localisation] Error in resource listener:\", err);\r\n      }\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Получает текущий языковой код\r\n   * @returns {string}\r\n   * @see https://www.i18next.com/overview/api#language\r\n   */\r\n  get lang() {\r\n    return this.lib?.language ?? \"\";\r\n  }\r\n\r\n  /**\r\n   * Получает все доступные языковые коды\r\n   * @returns {Array}\r\n   * @see https://www.i18next.com/overview/api#languages\r\n   */\r\n  get langList() {\r\n    return Object.keys(this.lib?.services?.resourceStore?.data ?? []);\r\n  }\r\n\r\n  #onReady() {\r\n    if (!Localisation.instance) {\r\n      Localisation.instance = this;\r\n    }\r\n\r\n    Localisation.states.isReady = true;\r\n\r\n    // всплывающее событие готовности плагина\r\n    if (!Localisation.isSSR) {\r\n      bubble(getDocument(), constants.localisationBubbles.localisationReady, { instance: this });\r\n    }\r\n\r\n    // вызов коллбэков готовности плагина\r\n    Localisation.#onReadyCallbacks.forEach((callback) => {\r\n      try {\r\n        callback(this);\r\n      } catch (err) {\r\n        console.error(\"[Localisation] Error in onReady callback:\", err);\r\n      }\r\n    });\r\n\r\n    // очистка коллбэков\r\n    Localisation.#onReadyCallbacks.clear();\r\n  }\r\n\r\n  #onError(args: unknown) {\r\n    Localisation.states.isError = true;\r\n    if (this.cfg.isDebug) {\r\n      console.warn(Localisation.#getErrorMsg(\"custom\"), args);\r\n    }\r\n    if (!Localisation.isSSR) {\r\n      bubble(getDocument(), constants.localisationBubbles.localisationError, { instance: this, err: args });\r\n    }\r\n    if (typeof this.cfg.onError === \"function\") {\r\n      try {\r\n        this.cfg.onError(this, args);\r\n      } catch (err) {\r\n        console.error(\"[Localisation] Error in onError callback:\", err);\r\n      }\r\n    }\r\n  }\r\n\r\n  #onChange(lang: string) {\r\n    if (!Localisation.isSSR) {\r\n      bubble(getDocument(), constants.localisationBubbles.localisationChange, { instance: this, lang });\r\n    }\r\n    if (typeof this.cfg.onChange === \"function\") {\r\n      try {\r\n        this.cfg.onChange(this, lang);\r\n      } catch (err) {\r\n        console.error(\"[Localisation] Error in onChange callback:\", err);\r\n      }\r\n    }\r\n  }\r\n\r\n  #onMiss(args: unknown) {\r\n    if (!Localisation.isSSR) {\r\n      bubble(getDocument(), constants.localisationBubbles.localisationMiss, { instance: this, miss: args });\r\n    }\r\n    if (typeof this.cfg.onMiss === \"function\") {\r\n      try {\r\n        this.cfg.onMiss(this, args);\r\n      } catch (err) {\r\n        console.error(\"[Localisation] Error in onMiss callback:\", err);\r\n      }\r\n    }\r\n  }\r\n\r\n  #bindEvents() {\r\n    this.lib.on(\"missingKey\", (...args) => this.#onMiss(args));\r\n    this.lib.on(\"languageChanged\", (lang) => this.#onChange(lang));\r\n    this.lib.on(\"failedLoading\", (...args) => this.#onError(args));\r\n    this.lib.on(\"initialized\", () => this.#onReady());\r\n  }\r\n\r\n  /**\r\n   * Получает язык с кодом региона в формате `ISO-3166`\r\n   * @param lang{String} код языка (например, \"ru\", \"en\")\r\n   * @returns {String|null} язык с регионом (например, \"ru_RU\", \"en_US\") или null\r\n   */\r\n  static getLangWithRegion(lang: unknown) {\r\n    if (typeof lang !== \"string\" || lang.length === 0) {\r\n      return null;\r\n    }\r\n\r\n    // Уже в правильном формате?\r\n    if (/^[a-z]{2}_[A-Z]{2}$/.test(lang)) {\r\n      return lang;\r\n    }\r\n\r\n    // Попробуем разобрать входной код\r\n    const parts = lang.split(/[_-]/);\r\n    let language = parts[0]?.toLowerCase();\r\n\r\n    if (!language) {\r\n      console.error(`[Localisation] Invalid language code: ${lang}`);\r\n      return null;\r\n    }\r\n\r\n    // Обработка специальных случаев\r\n    switch (language) {\r\n      case \"en\":\r\n        return \"en_US\"; // en -> en_US\r\n      case \"rus\":\r\n        language = \"ru\";\r\n        break;\r\n    }\r\n\r\n    // Если у нас уже есть регион\r\n    if (parts.length >= 2) {\r\n      const region = parts[1]?.toUpperCase();\r\n      if (region && /^[A-Z]{2}$/.test(region)) {\r\n        const isoRegion = iso.whereAlpha2(region)?.alpha2;\r\n        if (isoRegion) {\r\n          return `${language}_${isoRegion}`;\r\n        }\r\n      }\r\n    }\r\n\r\n    // По умолчанию используем язык как регион (например: fr -> fr_FR)\r\n    const isoRegion = iso.whereAlpha2(language)?.alpha2;\r\n    if (isoRegion) {\r\n      return `${language}_${isoRegion}`;\r\n    }\r\n\r\n    console.error(`[Localisation] Can't find ISO-3166 region for code: ${lang}`);\r\n    return null;\r\n  }\r\n\r\n  static #getResolvedLang(str: string) {\r\n    const [ lang ] = str.split(\"-\");\r\n    return !!lang ? lang.toLowerCase() : constants.defaultLangKeys[0];\r\n  }\r\n\r\n  /**\r\n   * Получает язык, указанный разработчиком для пользователя (из атрибута lang документа)\r\n   * @returns {String} код языка\r\n   */\r\n  #getUserLanguage() {\r\n    if (Localisation.isSSR) {\r\n      return constants.defaultLangKeys[0];\r\n    }\r\n    const htmlLang = getDocument().documentElement.lang;\r\n    if (htmlLang) {\r\n      return Localisation.#getResolvedLang(htmlLang);\r\n    } else {\r\n      return constants.defaultLangKeys[0];\r\n    }\r\n  }\r\n\r\n  async #init() {\r\n    if (Localisation.states.isLoading) {\r\n      if (Localisation.#loadingPromise) {\r\n        return Localisation.#loadingPromise.then(() => undefined);\r\n      }\r\n      return;\r\n    }\r\n\r\n    Localisation.states.isLoading = true;\r\n    this.#bindEvents();\r\n\r\n    const {\r\n      isDebug = false,\r\n      fallbackNS,\r\n      locale,\r\n      locales,\r\n      langKeys,\r\n    } = this.cfg;\r\n\r\n    return await i18next\r\n      .use(initReactI18next)\r\n      .init({\r\n        fallbackLng: langKeys,\r\n        fallbackNS,\r\n        supportedLngs: langKeys,\r\n        lng: locale,\r\n        debug: isDebug,\r\n        interpolation: {\r\n          escapeValue: false,\r\n        },\r\n        resources: Localisation.utils.getResourcesFromObj({\r\n          data: getMergedObj(locales, constants.defaultLocales, { isMergeArrays: false }),\r\n          langKeys,\r\n        }),\r\n      });\r\n  }\r\n\r\n  /**\r\n   * Загружает новое пространство имён\r\n   * @param ns{String} название пространства имён\r\n   * @param onError{Function} обработчик ошибок\r\n   * @returns {Promise<Localisation>} промис с экземпляром плагина\r\n   * @see https://www.i18next.com/principles/namespaces\r\n   */\r\n  async getNewNS(ns: string | readonly string[], onError?: (error: unknown) => void): Promise<Localisation> {\r\n    if (!ns) {\r\n      throw new Error(Localisation.#getErrorMsg(\"ns\"));\r\n    }\r\n\r\n    // Собираем информацию о добавляемых namespace\r\n    const nsArray = Array.isArray(ns) ? ns : [ ns ];\r\n\r\n    await this.lib.loadNamespaces(ns, onError);\r\n\r\n    // Уведомляем о добавлении ресурсов для каждого namespace\r\n    nsArray.forEach((namespace) => {\r\n      const bundle = this.lib.getResourceBundle(this.lang, namespace);\r\n      const keys = bundle ? Object.keys(bundle) : [];\r\n      Localisation.#notifyResourceAdded({\r\n        lang: this.lang,\r\n        ns: namespace,\r\n        keys,\r\n        source: \"getNewNS\",\r\n      });\r\n    });\r\n\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Меняет текущий язык интерфейса\r\n   * @param lang{String=} код языка, по умолчанию язык пользователя\r\n   * @param callback{Function=} callback-функция\r\n   * @returns {Promise<Localisation>} промис с экземпляром плагина\r\n   * @see https://www.i18next.com/overview/api#changelanguage\r\n   */\r\n  async getChangeLanguage(lang = this.#getUserLanguage(), callback?: (error: unknown) => void): Promise<Localisation> {\r\n    await this.lib.changeLanguage(lang, callback);\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Проверяет существование локали по ключу\r\n   * @param key{String=} название ключа (например, \"forms.invalidInput\")\r\n   * @param options{Object=} опции проверки\r\n   * @returns {Boolean} результат проверки\r\n   * @see https://www.i18next.com/overview/api#exists\r\n   */\r\n  isLocaleExist(key = \"\", options: Record<string, unknown> = {}): boolean {\r\n    return this.lib.exists(key, options);\r\n  }\r\n\r\n  /**\r\n   * Получает локализованную строку по ключу\r\n   * @param key{String|String[]=} название ключа или массив ключей (например, \"forms.invalidInput\")\r\n   * @param options{Object=} опции получения (интерполяция, контекст и т.д.)\r\n   * @returns {String} локализованная строка или пустая строка, если ключ не найден\r\n   * @see https://www.i18next.com/overview/api#t\r\n   */\r\n  getLocale(key: string | string[] = \"\", options: Record<string, unknown> = {}) {\r\n    const keysToCheck = Array.isArray(key) ? key : [ key ];\r\n    const isExist = keysToCheck.some((keyToCheck) => {\r\n      return this.isLocaleExist(keyToCheck, options);\r\n    });\r\n    if (isExist) {\r\n      return String(this.lib.t(key, options));\r\n    } else {\r\n      return \"\";\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Получает функцию локализации с фиксированными параметрами\r\n   * @param code{String|String[]} код локали\r\n   * @param lang{String=} код языка\r\n   * @param ns{String=} пространство имён\r\n   * @returns {Function} функция для получения локализованных строк\r\n   * @see https://www.i18next.com/overview/api#getfixedt\r\n   */\r\n  getLocaleFn(code: string | string[], lang = this.lang, ns = this.cfg.fallbackNS): TLocaleFn {\r\n    const fixedT = this.lib.getFixedT(lang, ns);\r\n\r\n    const withPrefix = (key: string): string => {\r\n      if (Array.isArray(code)) {\r\n        return code.length ? `${code.join(\".\")}.${key}` : key;\r\n      }\r\n      return code ? `${code}.${key}` : key;\r\n    };\r\n\r\n    return (key, options = {}) => {\r\n      const resolvedKey = Array.isArray(key) ? key.map(withPrefix) : withPrefix(key);\r\n      return String(fixedT(resolvedKey, options));\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Получает ресурс локализации по пространству имён\r\n   * @param namespace{String} пространство имён\r\n   * @param lang{String=} код языка\r\n   * @param ns{String=} пространство имён\r\n   * @returns {Object} объект ресурса\r\n   */\r\n  getResource(namespace: string, lang: string | undefined = this.lang, ns: string | undefined = this.cfg.fallbackNS) {\r\n    return this.lib.getResource(lang, ns, namespace);\r\n  }\r\n\r\n  /**\r\n   * Добавляет новые данные локализации для текущих языков\r\n   * @returns {Promise<Localisation>}\r\n   * @see https://www.i18next.com/overview/api#addresources\r\n   */\r\n  async getResourceAppend(locales: TLocaleSource = {}, isFlat = true, isSilent = false): Promise<Localisation> {\r\n    const data = Localisation.utils.getResourcesFromObj({\r\n      data: locales,\r\n      langKeys: this.cfg.langKeys,\r\n    });\r\n\r\n    for (const [ lang, typeStorage ] of Object.entries(data)) {\r\n      const langResources = typeStorage[this.cfg.fallbackNS];\r\n\r\n      if (!langResources) {\r\n        continue;\r\n      }\r\n\r\n      if (isFlat) {\r\n        const flatValues = Localisation.utils.getFlattenLanguageResources(langResources);\r\n        const addedKeys = Object.keys(flatValues);\r\n\r\n        if (addedKeys.length > 0) {\r\n          this.lib.addResources(lang, this.cfg.fallbackNS, flatValues);\r\n\r\n          Localisation.#notifyResourceAdded({\r\n            isSilent,\r\n            lang,\r\n            ns: this.cfg.fallbackNS,\r\n            keys: addedKeys,\r\n            source: \"getResourceAppend\",\r\n          });\r\n        }\r\n      } else {\r\n        for (const [ ns, values ] of Object.entries(langResources)) {\r\n          const addedKeys = Localisation.utils.isRecord(values) ? Object.keys(values) : [];\r\n          if (addedKeys.length > 0) {\r\n            this.lib.addResourceBundle(lang, ns, values);\r\n\r\n            Localisation.#notifyResourceAdded({\r\n              isSilent,\r\n              lang,\r\n              ns,\r\n              keys: addedKeys,\r\n              source: \"getResourceAppend\",\r\n            });\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    Localisation.states.isError = false;\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Добавляет новый ресурс локализации для указанного языка и пространства имён\r\n   * @param params{Object} параметры\r\n   * @param params.lang{String=} код языка\r\n   * @param params.ns{String=} пространство имён\r\n   * @param params.locales{Object=} объект локализации\r\n   * @param params.isOverride{Boolean=} заменять существующий ресурс\r\n   * @returns {Promise<Localisation>} промис с экземпляром плагина\r\n   * @see https://www.i18next.com/overview/api#addresourcebundle\r\n   */\r\n  async getNewResource({\r\n    lang = this.lang,\r\n    ns = this.cfg.fallbackNS,\r\n    locales = constants.defaultLocales,\r\n    isOverride = false,\r\n  }): Promise<Localisation> {\r\n    if (!ns) {\r\n      throw new Error(Localisation.#getErrorMsg(\"ns\"));\r\n    }\r\n\r\n    // Ждём готовности, если плагин ещё не инициализирован\r\n    if (!Localisation.isReady && Localisation.#loadingPromise) {\r\n      await Localisation.#loadingPromise;\r\n    }\r\n\r\n    if (this.lib.hasResourceBundle(lang, ns) && !isOverride) {\r\n      throw new Error(Localisation.#getErrorMsg(\"bundle\"));\r\n    }\r\n\r\n    // Собираем ключи, которые будут добавлены\r\n    const addedKeys = typeof locales === \"object\" && locales !== null\r\n      ? Object.keys(locales as Record<string, unknown>)\r\n      : [];\r\n\r\n    this.lib.addResourceBundle(lang, ns, locales);\r\n\r\n    // Уведомляем о добавлении ресурсов\r\n    Localisation.#notifyResourceAdded({\r\n      lang,\r\n      ns,\r\n      keys: addedKeys,\r\n      source: \"getNewResource\",\r\n    });\r\n\r\n    Localisation.states.isError = false;\r\n    return this;\r\n  }\r\n\r\n}\r\n"],"names":["Localisation","cfg","lib","static","localisationUtils","window","isReady","isError","isLoading","states","locale","langKeys","constants","defaultLangKeys","locales","isDebug","fallbackNS","load","onMiss","onChange","onReady","onError","type","prefix","constructor","name","getErrBody","Set","create","instance","Promise","resolve","loadingPromise","pendingInstance","Error","this","getCfg","i18next","addReadyCallback","length","split","console","error","getResolvedLang","getUserLanguage","init","then","isSSR","debug","language","languages","join","catch","err","finally","defaultCfg","callback","onReadyCallbacks","has","add","onResourceAdded","listener","resourceListeners","delete","notifyResourceAdded","data","eventData","isSilent","isHasCode","code","keys","some","key","startsWith","forEach","lang","langList","Object","services","resourceStore","bubble","getDocument","localisationBubbles","localisationReady","clear","args","warn","getErrorMsg","localisationError","localisationChange","localisationMiss","miss","bindEvents","on","getLangWithRegion","test","parts","toLowerCase","region","toUpperCase","isoRegion","iso","whereAlpha2","alpha2","str","htmlLang","documentElement","use","initReactI18next","fallbackLng","supportedLngs","lng","interpolation","escapeValue","resources","utils","getResourcesFromObj","getMergedObj","defaultLocales","isMergeArrays","getNewNS","ns","nsArray","Array","isArray","loadNamespaces","namespace","bundle","getResourceBundle","source","getChangeLanguage","changeLanguage","isLocaleExist","options","exists","getLocale","keysToCheck","isExist","keyToCheck","String","t","getLocaleFn","fixedT","getFixedT","withPrefix","resolvedKey","map","getResource","getResourceAppend","isFlat","typeStorage","entries","langResources","flatValues","getFlattenLanguageResources","addedKeys","addResources","values","isRecord","addResourceBundle","getNewResource","isOverride","hasResourceBundle"],"mappings":"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"}