UNPKG

@yelon/theme

Version:

ng-yunzai theme system library.

1 lines 281 kB
{"version":3,"file":"theme.mjs","sources":["../../../../packages/theme/src/services/preloader/preloader.ts","../../../../packages/theme/src/services/i18n/i18n.ts","../../../../packages/theme/src/services/menu/menu.service.ts","../../../../packages/theme/src/services/settings/settings.service.ts","../../../../packages/theme/src/services/responsive/responsive.ts","../../../../packages/theme/src/services/rtl/rtl.service.ts","../../../../packages/theme/src/services/title/title.service.ts","../../../../packages/theme/src/services/i18n/i18n.pipe.ts","../../../../packages/theme/src/services/i18n/i18n-url.guard.ts","../../../../packages/theme/src/locale/languages/el-GR.ts","../../../../packages/theme/src/locale/languages/en-US.ts","../../../../packages/theme/src/locale/languages/es-ES.ts","../../../../packages/theme/src/locale/languages/fr-FR.ts","../../../../packages/theme/src/locale/languages/hr-HR.ts","../../../../packages/theme/src/locale/languages/it-IT.ts","../../../../packages/theme/src/locale/languages/ko-KR.ts","../../../../packages/theme/src/locale/languages/pl-PL.ts","../../../../packages/theme/src/locale/languages/sl-SI.ts","../../../../packages/theme/src/locale/languages/tr-TR.ts","../../../../packages/theme/src/locale/languages/zh-CN.ts","../../../../packages/theme/src/locale/languages/zh-TW.ts","../../../../packages/theme/src/services/i18n/lang.ts","../../../../packages/theme/src/locale/locale.tokens.ts","../../../../packages/theme/src/locale/locale.service.ts","../../../../packages/theme/src/services/http/http.client.ts","../../../../packages/theme/src/services/i18n/yunzai-i18n.ts","../../../../packages/theme/src/services/modal/modal.helper.ts","../../../../packages/theme/src/services/drawer/drawer.helper.ts","../../../../packages/theme/src/services/http/http.decorator.ts","../../../../packages/theme/src/services/http/http.token.ts","../../../../packages/theme/src/locale/locale.module.ts","../../../../packages/theme/src/locale/languages/ja-JP.ts","../../../../packages/theme/src/locale/languages/vi-VI.ts","../../../../packages/theme/src/locale/languages/ar-SA.ts","../../../../packages/theme/src/pipes/date/date.pipe.ts","../../../../packages/theme/src/pipes/keys/keys.pipe.ts","../../../../packages/theme/src/pipes/yn/yn.pipe.ts","../../../../packages/theme/src/pipes/safe/html.pipe.ts","../../../../packages/theme/src/pipes/safe/url.pipe.ts","../../../../packages/theme/src/icons.ts","../../../../packages/theme/src/theme.module.ts","../../../../packages/theme/src/provide.ts","../../../../packages/theme/src/router/optional-preloader.ts","../../../../packages/theme/src/version.ts","../../../../packages/theme/theme.ts"],"sourcesContent":["import { DOCUMENT, isPlatformServer } from '@angular/common';\nimport { PLATFORM_ID, inject } from '@angular/core';\n\nexport function stepPreloader(): () => void {\n const doc: Document = inject(DOCUMENT);\n const ssr = isPlatformServer(inject(PLATFORM_ID));\n if (ssr) {\n return () => {};\n }\n const body = doc.querySelector<HTMLBodyElement>('body')!;\n body.style.overflow = 'hidden';\n let done = false;\n\n return () => {\n if (done) return;\n\n done = true;\n const preloader = doc.querySelector<HTMLElement>('.preloader');\n if (preloader == null) return;\n\n const CLS = 'preloader-hidden';\n preloader.addEventListener('transitionend', () => {\n preloader.className = CLS;\n });\n preloader.className += ` ${CLS}-add ${CLS}-add-active`;\n body.style.overflow = '';\n };\n}\n","import { inject, Injectable, InjectionToken } from '@angular/core';\nimport { BehaviorSubject, Observable, filter } from 'rxjs';\n\nimport { YunzaiConfigService, YunzaiThemeI18nConfig } from '@yelon/util/config';\n\nexport interface YunzaiI18NService {\n [key: string]: any;\n\n /**\n * Call `use` to trigger change notification\n *\n * 调用 `use` 触发变更通知\n */\n readonly change: Observable<string>;\n\n /**\n * Get the default language\n *\n * 获取默认语言\n */\n readonly defaultLang: string;\n\n /**\n * Get current language\n *\n * 获取当前语言\n */\n readonly currentLang: string;\n\n /**\n * Change language\n *\n * 变更语言\n *\n * @param emit 是否触发 `change`,默认:true ; Should be removed, please use `change` event instead.\n */\n use(lang: string, data?: Record<string, unknown>): void;\n\n /**\n * Return to the current language list\n *\n * 返回当前语言列表\n */\n getLangs(): any[];\n\n /**\n * Translate 翻译\n *\n * @param params 模板所需要的参数对象\n * @param isSafe 是否返回安全字符,自动调用 `bypassSecurityTrustHtml`; Should be removed, If you need SafeHtml support, please use `| html` pipe instead.\n */\n fanyi(path: string, params?: unknown | unknown[]): string;\n}\n\nexport const YUNZAI_I18N_TOKEN = new InjectionToken<YunzaiI18NService>('yunzaiI18nToken', {\n providedIn: 'root',\n factory: () => new YunzaiI18NServiceFake()\n});\n\n@Injectable()\nexport abstract class YunzaiI18nBaseService implements YunzaiI18NService {\n protected readonly cogSrv = inject(YunzaiConfigService);\n private cog: YunzaiThemeI18nConfig;\n protected _change$ = new BehaviorSubject<string | null>(null);\n protected _currentLang: string = '';\n protected _defaultLang: string = '';\n protected _data: Record<string, string> = {};\n get change(): Observable<string> {\n return this._change$.asObservable().pipe(filter(w => w != null)) as Observable<string>;\n }\n get defaultLang(): string {\n return this._defaultLang;\n }\n get currentLang(): string {\n return this._currentLang;\n }\n get data(): Record<string, string> {\n return this._data;\n }\n\n constructor() {\n this.cog = this.cogSrv.merge('themeI18n', {\n interpolation: ['{{', '}}']\n })!;\n }\n\n /**\n * Flattened data source\n *\n * @example\n * {\n * \"name\": \"Name\",\n * \"sys\": {\n * \"\": \"System\",\n * \"title\": \"Title\"\n * }\n * }\n * =>\n * {\n * \"name\": \"Name\",\n * \"sys\": \"System\",\n * \"sys.title\": \"Title\"\n * }\n */\n flatData(data: Record<string, unknown>, parentKey: string[]): Record<string, string> {\n const res: Record<string, string> = {};\n for (const key of Object.keys(data)) {\n const value = data[key];\n if (typeof value === 'object') {\n const child = this.flatData(value as Record<string, unknown>, parentKey.concat(key));\n Object.keys(child).forEach(childKey => (res[childKey] = child[childKey]));\n } else {\n res[(key ? parentKey.concat(key) : parentKey).join('.')] = `${value}`;\n }\n }\n return res;\n }\n\n abstract use(lang: string, data?: Record<string, unknown>): void;\n\n abstract getLangs(): any;\n\n fanyi(path: string, params?: unknown | unknown[]): string {\n let content = this._data[path] || '';\n if (!content) return path;\n\n if (!params) return content;\n\n if (typeof params === 'object') {\n const interpolation = this.cog.interpolation!;\n const objParams = params as Record<string, unknown>;\n Object.keys(objParams).forEach(key => {\n content = content.replace(\n new RegExp(`${interpolation[0]}\\\\s?${key}\\\\s?${interpolation[1]}`, 'g'),\n `${objParams[key]}`\n );\n });\n }\n\n (Array.isArray(params) ? params : [params]).forEach(\n (item, index) => (content = content.replace(new RegExp(`\\\\{\\\\s?${index}\\\\s?\\\\}`, 'g'), `${item}`))\n );\n return content;\n }\n}\n\n@Injectable({ providedIn: 'root' })\nexport class YunzaiI18NServiceFake extends YunzaiI18nBaseService {\n use(lang: string, data: Record<string, unknown>): void {\n this._data = this.flatData(data ?? {}, []);\n this._currentLang = lang;\n this._change$.next(lang);\n }\n\n getLangs(): any[] {\n return [];\n }\n}\n","import { Injectable, OnDestroy, inject } from '@angular/core';\nimport { BehaviorSubject, Observable, Subscription, share } from 'rxjs';\n\nimport { ACLService } from '@yelon/acl';\n\nimport { Menu, MenuIcon, MenuInner } from './interface';\nimport { YUNZAI_I18N_TOKEN } from '../i18n/i18n';\n\n/**\n * 菜单服务,[在线文档](https://ng.yunzainfo.com/theme/menu)\n */\n@Injectable({ providedIn: 'root' })\nexport class MenuService implements OnDestroy {\n private readonly i18nSrv = inject(YUNZAI_I18N_TOKEN);\n private readonly aclService = inject(ACLService);\n private _change$: BehaviorSubject<Menu[]> = new BehaviorSubject<Menu[]>([]);\n private i18n$?: Subscription;\n private data: Menu[] = [];\n private $routerLink: BehaviorSubject<string> = new BehaviorSubject<string>('');\n\n /**\n * 是否完全受控菜单打开状态,默认:`false`\n */\n openStrictly = false;\n\n constructor() {\n this.i18n$ = this.i18nSrv.change.subscribe(() => this.resume());\n }\n\n get change(): Observable<Menu[]> {\n return this._change$.pipe(share());\n }\n\n get menus(): Menu[] {\n return this.data;\n }\n\n /**\n * Returns a default menu link\n *\n * 返回一个默认跳转的菜单链接\n */\n getDefaultRedirect(opt: { redirectUrl?: string } = {}): string | null | undefined {\n let ret: string | null | undefined;\n this.visit(this.menus, (item: MenuInner) => {\n if (typeof item.link !== 'string' || item.link.length <= 0 || !item._aclResult || item._hidden === true) {\n return;\n }\n if (ret == null || ret.length <= 0 || item.link == opt?.redirectUrl) {\n ret = item.link;\n }\n });\n return ret;\n }\n\n visit<T extends Menu = Menu>(data: T[], callback: (item: T, parentMenum: T | null, depth?: number) => void): void;\n visit(data: Menu[], callback: (item: Menu, parentMenum: Menu | null, depth?: number) => void): void;\n visit(data: Menu[], callback: (item: Menu, parentMenum: Menu | null, depth?: number) => void): void {\n const inFn = (list: Menu[], parentMenu: Menu | null, depth: number): void => {\n for (const item of list) {\n callback(item, parentMenu, depth);\n if (item.children && item.children.length > 0) {\n inFn(item.children, item, depth + 1);\n } else {\n item.children = [];\n }\n }\n };\n\n inFn(data, null, 0);\n }\n\n add(items: Menu[]): void {\n this.data = items;\n this.resume();\n }\n\n private fixItem(item: MenuInner): void {\n item._aclResult = true;\n if (!item.render_type) item.render_type = 'item';\n if (!item.link) item.link = '';\n if (!item.externalLink) item.externalLink = '';\n\n // badge\n if (item.badge) {\n if (item.badgeDot !== true) {\n item.badgeDot = false;\n }\n if (!item.badgeStatus) {\n item.badgeStatus = 'error';\n }\n }\n\n if (!Array.isArray(item.children)) {\n item.children = [];\n }\n\n // icon\n if (typeof item.icon === 'string') {\n let type = 'class';\n let value = item.icon;\n // compatible `anticon anticon-user`\n if (~item.icon.indexOf(`anticon-`)) {\n type = 'icon';\n value = value.split('-').slice(1).join('-');\n } else if (/^https?:\\/\\//.test(item.icon)) {\n type = 'img';\n }\n item.icon = { type, value } as any;\n }\n if (item.icon != null) {\n item.icon = { theme: 'outline', spin: false, ...(item.icon as MenuIcon) };\n }\n\n item.text = item.i18n ? this.i18nSrv.fanyi(item.i18n) : item.text;\n\n // group\n item.group = item.group !== false;\n\n // hidden\n item._hidden = typeof item.hide === 'undefined' ? false : item.hide;\n\n // disabled\n item.disabled = typeof item.disabled === 'undefined' ? false : item.disabled;\n\n // acl\n item._aclResult = item.acl ? this.aclService.can(item.acl) : true;\n\n item.open = item.open != null ? item.open : false;\n }\n\n /**\n * 重置菜单,可能I18N、用户权限变动时需要调用刷新\n */\n resume<T extends Menu = Menu>(callback?: (item: T, parentMenum: T | null, depth?: number) => void): void;\n resume(callback?: (item: Menu, parentMenum: Menu | null, depth?: number) => void): void;\n resume(callback?: (item: Menu, parentMenum: Menu | null, depth?: number) => void): void {\n let i = 1;\n const shortcuts: Menu[] = [];\n this.visit(this.data, (item: MenuInner, parent, depth) => {\n item._id = i++;\n item._parent = parent;\n item._depth = depth;\n this.fixItem(item);\n\n // shortcut\n if (parent && item.shortcut === true && parent.shortcutRoot !== true) {\n shortcuts.push(item);\n }\n\n if (callback) callback(item, parent, depth);\n });\n\n this.loadShortcut(shortcuts);\n this._change$.next(this.data);\n }\n\n /**\n * 加载快捷菜单,加载位置规则如下:\n * 1、统一在下标0的节点下(即【主导航】节点下方)\n * 1、若 children 存在 【shortcutRoot: true】则最优先【推荐】这种方式\n * 2、否则查找带有【dashboard】字样链接,若存在则在此菜单的下方创建快捷入口\n * 3、否则放在0节点位置\n */\n private loadShortcut(shortcuts: MenuInner[]): void {\n if (shortcuts.length === 0 || this.data.length === 0) {\n return;\n }\n\n const ls = this.data[0].children as MenuInner[];\n let pos = ls.findIndex(w => w.shortcutRoot === true);\n if (pos === -1) {\n pos = ls.findIndex(w => w.link!.includes('dashboard'));\n pos = (pos !== -1 ? pos : -1) + 1;\n const shortcutMenu = {\n text: '快捷菜单',\n i18n: 'shortcut',\n icon: 'icon-rocket',\n children: []\n } as MenuInner;\n this.data[0].children!.splice(pos, 0, shortcutMenu);\n }\n let _data = this.data[0].children![pos];\n if (_data.i18n) _data.text = this.i18nSrv.fanyi(_data.i18n);\n _data = Object.assign(_data, {\n shortcutRoot: true,\n _id: -1,\n _parent: null,\n _depth: 1\n } as MenuInner);\n _data.children = shortcuts.map(i => {\n i._depth = 2;\n i._parent = _data;\n return i;\n });\n }\n\n /**\n * 清空菜单\n */\n clear(): void {\n this.data = [];\n this._change$.next(this.data);\n }\n\n /**\n * Use `url` or `key` to find menus\n *\n * 利用 `url` 或 `key` 查找菜单\n */\n find(options: {\n key?: string | null;\n url?: string | null;\n recursive?: boolean | null;\n /**\n * When the callback returns a Boolean type, it means the custom validation result\n *\n * 当回调返回一个布尔类型时,表示自定义校验结果\n */\n cb?: ((i: Menu) => boolean | null) | null;\n /**\n * Use the current menu data by default\n *\n * 默认使用当前菜单数据\n */\n data?: Menu[] | null;\n /**\n * Whether to ignore hide items, default: `false`\n *\n * 是否忽略隐藏的项,默认:`false`\n */\n ignoreHide?: boolean;\n /**\n * Whether to return the last one, default: `false`\n *\n * 是否返回最后一个,默认:`false`\n */\n last?: boolean;\n }): Menu | null {\n const opt = { recursive: false, ignoreHide: false, last: false, ...options };\n if (opt.key != null) {\n return this.getItem(opt.key);\n }\n\n let url = opt.url;\n\n let item: Menu | null = null;\n\n while (!item && url) {\n this.visit(opt.data ?? this.data, i => {\n if (!opt.last && item != null) {\n return;\n }\n if (opt.ignoreHide && i.hide) {\n return;\n }\n if (opt.cb) {\n const res = opt.cb(i);\n if (typeof res === 'boolean' && res) {\n item = i;\n }\n }\n if (i.link != null && i.link === url) {\n item = i;\n }\n });\n\n if (!opt.recursive) break;\n\n if (/[?;]/g.test(url)) {\n url = url.split(/[?;]/g)[0];\n } else {\n url = url.split('/').slice(0, -1).join('/');\n }\n }\n\n return item;\n }\n\n /**\n * 根据url获取菜单列表\n * - 若 `recursive: true` 则会自动向上递归查找\n * - 菜单数据源包含 `/ware`,则 `/ware/1` 也视为 `/ware` 项\n */\n getPathByUrl(url: string, recursive: boolean = false): Menu[] {\n const ret: Menu[] = [];\n let item = this.find({ url, recursive }) as MenuInner;\n\n if (!item) return ret;\n\n do {\n ret.splice(0, 0, item);\n item = item._parent!;\n } while (item);\n\n return ret;\n }\n\n /**\n * Get menu based on `key`\n */\n getItem(key: string): Menu | null {\n let res: Menu | null = null;\n this.visit(this.data, item => {\n if (res == null && item.key === key) {\n res = item;\n }\n });\n return res;\n }\n\n /**\n * Set menu based on `key`\n */\n setItem(key: string | Menu, value: Menu, options?: { emit?: boolean }): void {\n const item = typeof key === 'string' ? this.getItem(key) : key;\n if (item == null) return;\n\n Object.keys(value).forEach(k => {\n item[k] = value[k];\n });\n this.fixItem(item);\n\n if (options?.emit !== false) this._change$.next(this.data);\n }\n\n /**\n * Open menu based on `key` or menu object\n */\n open(keyOrItem: string | Menu | null, options?: { emit?: boolean }): void {\n let item = typeof keyOrItem === 'string' ? this.find({ key: keyOrItem }) : keyOrItem;\n if (item == null) return;\n\n this.visit(this.menus, (i: MenuInner) => {\n i._selected = false;\n if (!this.openStrictly) i.open = false;\n });\n\n do {\n item._selected = true;\n item.open = true;\n item = item._parent;\n } while (item);\n if (options?.emit !== false) this._change$.next(this.data);\n }\n\n openAll(status?: boolean): void {\n this.toggleOpen(null, { allStatus: status });\n }\n\n toggleOpen(keyOrItem: string | Menu | null, options?: { allStatus?: boolean; emit?: boolean }): void {\n let item = typeof keyOrItem === 'string' ? this.find({ key: keyOrItem }) : keyOrItem;\n if (item == null) {\n this.visit(this.menus, (i: MenuInner) => {\n i._selected = false;\n i.open = options?.allStatus === true;\n });\n } else {\n if (!this.openStrictly) {\n this.visit(this.menus, (i: MenuInner) => {\n if (i !== item) i.open = false;\n });\n let pItem = item._parent;\n while (pItem) {\n pItem.open = true;\n pItem = pItem._parent;\n }\n }\n item.open = !item.open;\n }\n if (options?.emit !== false) this._change$.next(this.data);\n }\n\n ngOnDestroy(): void {\n this._change$.unsubscribe();\n this.i18n$?.unsubscribe();\n }\n\n setRouterLink(url: string): void {\n this.$routerLink.next(url);\n }\n\n getRouterLink(): Observable<string> {\n return this.$routerLink.asObservable();\n }\n}\n","import { Platform } from '@angular/cdk/platform';\nimport { Injectable, InjectionToken, Provider, inject } from '@angular/core';\nimport { Observable, Subject } from 'rxjs';\n\nimport { App, Layout, SettingsNotify, User } from './types';\n\nexport interface SettingsKeys {\n /** Layout data specifies the stored key, default: `layout` */\n layout: string;\n /** User data specifies the stored key, default: `user` */\n user: string;\n /** App data specifies the stored key, default: `app` */\n app: string;\n}\n\nexport const YUNZAI_SETTING_KEYS = new InjectionToken<SettingsKeys>('YUNZAI_SETTING_KEYS');\nexport const YUNZAI_SETTING_DEFAULT: Provider = {\n provide: YUNZAI_SETTING_KEYS,\n useValue: {\n layout: 'layout',\n user: 'user',\n app: 'app'\n }\n};\n\n@Injectable({ providedIn: 'root' })\nexport class SettingsService<L extends Layout = Layout, U extends User = User, A extends App = App> {\n private readonly KEYS = inject(YUNZAI_SETTING_KEYS);\n private readonly platform = inject(Platform);\n\n private notify$ = new Subject<SettingsNotify>();\n private _app: A | null = null;\n private _user: U | null = null;\n private _layout: L | null = null;\n\n getData(key: string): any {\n if (!this.platform.isBrowser) {\n return null;\n }\n return JSON.parse(localStorage.getItem(key) || 'null') || null;\n }\n\n setData(key: string, value: any): void {\n if (!this.platform.isBrowser) {\n return;\n }\n localStorage.setItem(key, JSON.stringify(value));\n }\n\n get layout(): L {\n if (!this._layout) {\n this._layout = {\n fixed: true,\n collapsed: false,\n boxed: false,\n lang: null,\n ...this.getData(this.KEYS.layout)\n };\n this.setData(this.KEYS.layout, this._layout);\n }\n return this._layout as L;\n }\n\n get app(): A {\n if (!this._app) {\n this._app = {\n year: new Date().getFullYear(),\n ...this.getData(this.KEYS.app)\n };\n this.setData(this.KEYS.app, this._app);\n }\n return this._app as A;\n }\n\n get user(): U {\n if (!this._user) {\n this._user = { ...this.getData(this.KEYS.user) };\n this.setData(this.KEYS.user, this._user);\n }\n return this._user as U;\n }\n\n get notify(): Observable<SettingsNotify> {\n return this.notify$.asObservable();\n }\n\n setLayout<T extends Layout = Layout>(name: T, value?: any): boolean;\n setLayout(name: string | L, value?: any): boolean;\n setLayout(name: string | L, value?: any): boolean {\n if (typeof name === 'string') {\n (this.layout as Layout)[name] = value;\n } else {\n this._layout = name;\n }\n this.setData(this.KEYS.layout, this._layout);\n this.notify$.next({ type: 'layout', name, value } as any);\n return true;\n }\n getLayout<T>(): T {\n return this._layout as unknown as T;\n }\n\n setApp<T extends App = App>(value: T): void;\n setApp(value: A): void;\n setApp(value: A): void {\n this._app = value;\n this.setData(this.KEYS.app, value);\n this.notify$.next({ type: 'app', value });\n }\n getApp<T>(): T {\n return this._app as unknown as T;\n }\n\n setUser<T extends User = User>(value: T): void;\n setUser(value: U): void;\n setUser(value: U): void {\n this._user = value;\n this.setData(this.KEYS.user, value);\n this.notify$.next({ type: 'user', value });\n }\n getUser<T>(): T {\n return this._user as unknown as T;\n }\n}\n","import { inject, Injectable } from '@angular/core';\n\nimport { YunzaiConfigService, YunzaiThemeResponsiveConfig } from '@yelon/util/config';\n\nexport const REP_MAX = 6;\nexport const SPAN_MAX = 24;\n\nexport type REP_TYPE = 1 | 2 | 3 | 4 | 5 | 6;\n\n@Injectable({ providedIn: 'root' })\nexport class ResponsiveService {\n private readonly cogSrv = inject(YunzaiConfigService);\n private cog: YunzaiThemeResponsiveConfig;\n constructor() {\n this.cog = this.cogSrv.merge('themeResponsive', {\n rules: {\n 1: { xs: 24 },\n 2: { xs: 24, sm: 12 },\n 3: { xs: 24, sm: 12, md: 8 },\n 4: { xs: 24, sm: 12, md: 8, lg: 6 },\n 5: { xs: 24, sm: 12, md: 8, lg: 6, xl: 4 },\n 6: { xs: 24, sm: 12, md: 8, lg: 6, xl: 4, xxl: 2 }\n }\n })!;\n if (\n Object.keys(this.cog.rules)\n .map(i => +i)\n .some((i: number) => i < 1 || i > REP_MAX)\n ) {\n throw new Error(`[theme] the responseive rule index value range must be 1-${REP_MAX}`);\n }\n }\n\n genCls(count: number, defaultCol: number = 1): string[] {\n const rule = { ...this.cog.rules[count > REP_MAX ? REP_MAX : Math.max(count, 1)] };\n const antColClass = 'ant-col';\n\n const itemMaxSpan = SPAN_MAX / defaultCol;\n const paddingSpan = (value: number | undefined): number | undefined => {\n if (value == null || defaultCol <= 1 || count >= defaultCol) return value;\n return Math.max(value, count * itemMaxSpan);\n };\n const clsMap = [`${antColClass}-xs-${paddingSpan(rule.xs)}`];\n if (rule.sm) clsMap.push(`${antColClass}-sm-${paddingSpan(rule.sm)}`);\n if (rule.md) clsMap.push(`${antColClass}-md-${paddingSpan(rule.md)}`);\n if (rule.lg) clsMap.push(`${antColClass}-lg-${paddingSpan(rule.lg)}`);\n if (rule.xl) clsMap.push(`${antColClass}-xl-${paddingSpan(rule.xl)}`);\n if (rule.xxl) clsMap.push(`${antColClass}-xxl-${paddingSpan(rule.xxl)}`);\n return clsMap;\n }\n}\n","import { Direction, Directionality } from '@angular/cdk/bidi';\nimport { Platform } from '@angular/cdk/platform';\nimport { DOCUMENT } from '@angular/common';\nimport { Injectable, inject } from '@angular/core';\nimport { Observable, filter, map } from 'rxjs';\n\nimport { YunzaiConfigService } from '@yelon/util/config';\nimport { NzConfigService } from 'ng-zorro-antd/core/config';\n\nimport { SettingsService } from '../settings/settings.service';\n\nexport const HTML_DIR = 'dir';\nexport const RTL_DIRECTION = 'direction';\nexport const RTL_NZ_COMPONENTS = ['modal', 'drawer', 'message', 'notification', 'image'];\nexport const RTL_YELON_COMPONENTS = ['loading', 'onboarding'];\nexport const LTR = 'ltr';\nexport const RTL = 'rtl';\n\n@Injectable({ providedIn: 'root' })\nexport class RTLService {\n private readonly d = inject(Directionality);\n private readonly nz = inject(NzConfigService);\n private readonly yelon = inject(YunzaiConfigService);\n private readonly platform = inject(Platform);\n private readonly doc = inject(DOCUMENT);\n private readonly srv = inject(SettingsService);\n\n private _dir: Direction = LTR;\n /**\n * Get or Set the current text direction\n *\n * 获取或设置当前文字方向\n */\n get dir(): Direction {\n return this._dir;\n }\n set dir(value: Direction) {\n this._dir = value;\n this.updateLibConfig();\n this.updateHtml();\n // Should be wait inited\n Promise.resolve().then(() => {\n this.d.valueSignal.set(value);\n this.d.change.emit(value);\n this.srv.setLayout(RTL_DIRECTION, value);\n });\n }\n\n /**\n * Get the next text direction\n *\n * 获取下一次文字方向\n */\n get nextDir(): Direction {\n return this.dir === LTR ? RTL : LTR;\n }\n\n /**\n * Subscription change notification\n *\n * 订阅变更通知\n */\n get change(): Observable<Direction> {\n return this.srv.notify.pipe(\n filter(w => w.name === RTL_DIRECTION),\n map(v => v.value)\n );\n }\n\n constructor() {\n this.dir = this.srv.layout.direction === RTL ? RTL : LTR;\n }\n\n /**\n * Toggle text direction\n *\n * 切换文字方向\n */\n toggle(): void {\n this.dir = this.nextDir;\n }\n\n private updateHtml(): void {\n if (!this.platform.isBrowser) {\n return;\n }\n const htmlEl = this.doc.querySelector('html') as HTMLElement;\n if (htmlEl) {\n const dir = this.dir;\n htmlEl.style.direction = dir;\n htmlEl.classList.remove(RTL, LTR);\n htmlEl.classList.add(dir);\n htmlEl.setAttribute(HTML_DIR, dir);\n }\n }\n\n private updateLibConfig(): void {\n RTL_NZ_COMPONENTS.forEach(name => {\n this.nz.set(name as any, { nzDirection: this.dir });\n });\n RTL_YELON_COMPONENTS.forEach(name => {\n this.yelon.set(name as any, { direction: this.dir });\n });\n }\n}\n","import { DOCUMENT } from '@angular/common';\nimport { DestroyRef, Injectable, Injector, OnDestroy, inject } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { Title } from '@angular/platform-browser';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { Observable, of, map, delay, isObservable, switchMap, Subscription } from 'rxjs';\n\nimport { YUNZAI_I18N_TOKEN } from '../i18n/i18n';\nimport { MenuService } from '../menu/menu.service';\n\nexport interface RouteTitle {\n title?: string | Observable<string>;\n titleI18n?: string;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class TitleService implements OnDestroy {\n private destroy$ = inject(DestroyRef);\n private _prefix: string = '';\n private _suffix: string = '';\n private _separator: string = ' - ';\n private _reverse: boolean = false;\n private tit$?: Subscription;\n\n readonly DELAY_TIME = 25;\n\n private readonly doc = inject(DOCUMENT);\n private readonly injector = inject(Injector);\n private readonly title = inject(Title);\n private readonly menuSrv = inject(MenuService);\n private readonly i18nSrv = inject(YUNZAI_I18N_TOKEN);\n\n constructor() {\n this.i18nSrv.change.pipe(takeUntilDestroyed()).subscribe(() => this.setTitle());\n }\n\n /**\n * Set separator\n *\n * 设置分隔符\n */\n set separator(value: string) {\n this._separator = value;\n }\n\n /**\n * Set prefix\n *\n * 设置前缀\n */\n set prefix(value: string) {\n this._prefix = value;\n }\n\n /**\n * Set suffix\n *\n * 设置后缀\n */\n set suffix(value: string) {\n this._suffix = value;\n }\n\n /**\n * Set whether to reverse\n *\n * 设置是否反转\n */\n set reverse(value: boolean) {\n this._reverse = value;\n }\n\n /**\n * Set the default CSS selector string\n *\n * 设置默认CSS选择器字符串\n */\n selector?: string | null;\n\n /**\n * Set default title name\n *\n * 设置默认标题名\n */\n default = `Not Page Name`;\n\n private getByElement(): Observable<string> {\n return of('').pipe(\n delay(this.DELAY_TIME),\n map(() => {\n const el = ((this.selector != null ? this.doc.querySelector(this.selector) : null) ||\n this.doc.querySelector('.yunzai-default__content-title h1') ||\n this.doc.querySelector('.page-header__title')) as HTMLElement;\n if (el) {\n let text = '';\n el.childNodes.forEach(val => {\n if (!text && val.nodeType === 3) {\n text = val.textContent!.trim();\n }\n });\n return text || el.firstChild!.textContent!.trim();\n }\n return '';\n })\n );\n }\n\n private getByRoute(): Observable<string> {\n let next = this.injector.get(ActivatedRoute);\n while (next.firstChild) next = next.firstChild;\n const data: RouteTitle = (next.snapshot && next.snapshot.data) || {};\n if (data.titleI18n) data.title = this.i18nSrv.fanyi(data.titleI18n);\n return isObservable(data.title) ? data.title : of(data.title!);\n }\n\n private getByMenu(): Observable<string> {\n const menus = this.menuSrv.getPathByUrl(this.injector.get<Router>(Router).url);\n if (!menus || menus.length <= 0) return of('');\n\n const item = menus[menus.length - 1];\n let title;\n if (item.i18n) title = this.i18nSrv.fanyi(item.i18n);\n return of(title || item.text!);\n }\n\n /**\n * Set the document title\n */\n setTitle(title?: string | string[]): void {\n this.tit$?.unsubscribe();\n this.tit$ = of(title)\n .pipe(\n switchMap(tit => (tit ? of(tit) : this.getByRoute())),\n switchMap(tit => (tit ? of(tit) : this.getByMenu())),\n switchMap(tit => (tit ? of(tit) : this.getByElement())),\n map(tit => tit || this.default),\n map(title => (!Array.isArray(title) ? [title] : title)),\n takeUntilDestroyed(this.destroy$)\n )\n .subscribe(titles => {\n let newTitles: string[] = [];\n if (this._prefix) {\n newTitles.push(this._prefix);\n }\n newTitles.push(...titles.filter(title => !!title));\n if (this._suffix) {\n newTitles.push(this._suffix);\n }\n if (this._reverse) {\n newTitles = newTitles.reverse();\n }\n this.title.setTitle(newTitles.join(this._separator));\n });\n }\n\n /**\n * Set i18n key of the document title\n */\n setTitleByI18n(key: string, params?: unknown): void {\n this.setTitle(this.i18nSrv.fanyi(key, params));\n }\n\n ngOnDestroy(): void {\n this.tit$?.unsubscribe();\n }\n}\n","import { Pipe, PipeTransform, inject } from '@angular/core';\n\nimport { YUNZAI_I18N_TOKEN } from './i18n';\n\n@Pipe({ name: 'i18n' })\nexport class I18nPipe implements PipeTransform {\n private readonly i18n = inject(YUNZAI_I18N_TOKEN);\n\n transform(key: string, params?: unknown | unknown[]): string {\n return this.i18n.fanyi(key, params);\n }\n}\n","import { Injectable, inject } from '@angular/core';\nimport { ActivatedRouteSnapshot, CanActivateChildFn, CanActivateFn } from '@angular/router';\nimport { Observable, of } from 'rxjs';\n\nimport { YunzaiConfigService } from '@yelon/util/config';\n\nimport { YUNZAI_I18N_TOKEN } from './i18n';\n\n@Injectable({ providedIn: 'root' })\nexport class YunzaiI18NGuardService {\n private readonly i18nSrv = inject(YUNZAI_I18N_TOKEN, { optional: true });\n private readonly cogSrv = inject(YunzaiConfigService);\n\n process(route: ActivatedRouteSnapshot): Observable<boolean> {\n const lang = route.params && route.params[this.cogSrv.get('themeI18n')?.paramNameOfUrlGuard ?? 'i18n'];\n if (lang != null) {\n this.i18nSrv?.use(lang);\n }\n return of(true);\n }\n}\n\n/**\n * Internationalization guard, automatically recognizes the language in Url and triggers the `YUNZAI_I18N_TOKEN.use` method\n *\n * 国际化守卫,自动识别Url中的语言,并触发 `YUNZAI_I18N_TOKEN.use` 方法\n *\n * ```ts\n * data: {\n * path: 'home',\n * canActivate: [ yunzaiI18nCanActivate ]\n * }\n * ```\n */\nexport const yunzaiI18nCanActivate: CanActivateFn = childRoute => inject(YunzaiI18NGuardService).process(childRoute);\n\n/**\n * Internationalization guard, automatically recognizes the language in Url and triggers the `YUNZAI_I18N_TOKEN.use` method\n *\n * 国际化守卫,自动识别Url中的语言,并触发 `YUNZAI_I18N_TOKEN.use` 方法\n *\n * ```ts\n * data: {\n * path: 'home',\n * canActivateChild: [ yunzaiI18nCanActivateChild ]\n * }\n * ```\n */\nexport const yunzaiI18nCanActivateChild: CanActivateChildFn = route => inject(YunzaiI18NGuardService).process(route);\n","import { FullLocaleData } from '../locale.types';\n\nexport default {\n abbr: 'el-GR',\n exception: {\n 403: `Λυπούμαστε, δεν έχετε πρόσβαση σε αυτήν τη σελίδα`,\n 404: `Λυπούμαστε, η σελίδα αυτή δεν βρέθηκε`,\n 500: `Λυπούμαστε, σφάλμα διακομιστή`,\n backToHome: 'Επιστροφή στην αρχική σελίδα'\n },\n noticeIcon: {\n emptyText: 'Δεν υπάρχουν δεδομένα',\n clearText: 'Καθαρισμός'\n },\n reuseTab: {\n close: 'Κλείσιμο καρτέλας',\n closeOther: 'Κλείσιμο των άλλων καρτέλων',\n closeRight: 'Κλείσιμο των καρτελών δεξιά',\n refresh: 'Ανανέωση'\n },\n tagSelect: {\n expand: 'Επέκταση',\n collapse: 'Σύμπτυξη'\n },\n miniProgress: {\n target: 'Στόχος: '\n },\n st: {\n total: '{{range[0]}} - {{range[1]}} από {{total}}',\n filterConfirm: 'ΟΚ',\n filterReset: 'Επαναφορά'\n },\n sf: {\n submit: 'Υποβολή',\n reset: 'Επαναφορά',\n search: 'Αναζήτηση',\n edit: 'Αποθήκευση',\n addText: 'Προσθήκη',\n removeText: 'Αφαίρεση',\n checkAllText: 'Επιλογή όλων',\n error: {\n 'false schema': `Η δυαδική δομή είναι ψευδής`,\n $ref: `Δεν είναι δυνατή η επίλυση της αναφοράς {ref}`,\n additionalItems: `Δεν πρέπει να έχει περισσότερα από {limit} στοιχεία`,\n additionalProperties: `Δεν πρέπει να έχει επιπλέον χαρακτηριστικά`,\n anyOf: `Πρέπει να ταιριάζει με κάποια απο τις δομές στο \"anyOf\"`,\n dependencies: `τα χαρακτηριστικά {deps} είναι απαραίτητα, όταν υπάρχει το χαρακτηριστικό {property}`,\n enum: `Πρέπει να είναι ίσο με μία από τις προκαθορισμένες τιμές`,\n format: `Πρέπει να έχει την μορφή \"{format}\"`,\n type: `Πρέπει να είναι {type}`,\n required: `Απαιτείται`,\n maxLength: `Δεν πρέπει να είναι μεγαλύτερο από {limit} χαρακτήρες`,\n minLength: `Δεν πρέπει να είναι μικρότερο από {limit} χαρακτήρες`,\n minimum: `Πρέπει να είναι {comparison} {limit}`,\n formatMinimum: `Πρέπει να είναι {comparison} {limit}`,\n maximum: `Πρέπει να είναι {comparison} {limit}`,\n formatMaximum: `Πρέπει να είναι {comparison} {limit}`,\n maxItems: `Δεν πρέπει να έχει περισσότερα από {limit} στοιχεία`,\n minItems: `Δεν πρέπει να έχει λιγότερα από {limit} στοιχεία`,\n maxProperties: `Δεν πρέπει να έχει περισσότερα από {limit} χαρακτηριστικά`,\n minProperties: `Δεν πρέπει να έχει λιγότερα από {limit} χαρακτηριστικά`,\n multipleOf: `Πρέπει να είναι πολλαπλάσιο του {multipleOf}`,\n not: `Δεν πρέπει να είναι εγκύρο, σύμφωνα με την δομή στο \"not\"`,\n oneOf: `Πρέπει να ταιριάζει με ακριβώς μια απο τις δομές στο \"oneOf\"`,\n pattern: `Πρέπει να ταιριάζει με το πρότυπο \"{pattern}\"`,\n uniqueItems: `Τα στοιχεία δεν πρέπει να επαναλαμβάνονται (τα στοιχεία ## {j} και {i} είναι ίδια)`,\n custom: `Πρέπει να έχει την μορφή`,\n propertyNames: `Το όνομα του χαρακτηριστικού \"{propertyName}\" δεν είναι έγκυρο`,\n patternRequired: `Πρέπει να υπάρχει το χαρακτηριστικό αντιπαραβολής προτύπου \"{missingPattern}\"`,\n switch: `Πρέπει να περάσει ο έλεγχος εγκυρότητας της λέξης-κλειδιού με την χρήση της \"switch\", η περίπτωση {caseIndex} αποτυγχάνει`,\n const: `Πρέπει να είναι ίσο με σταθερά`,\n contains: `Πρέπει να περιέχει κάποιο έγκυρο στοιχείο`,\n formatExclusiveMaximum: `formatExclusiveMaximum πρέπει να είναι boolean`,\n formatExclusiveMinimum: `formatExclusiveMinimum πρέπει να είναι boolean`,\n if: `Πρέπει να ταιριάζει στην δομή \"{failingKeyword}\"`\n }\n },\n onboarding: {\n skip: `Παράλειψη`,\n prev: `Προηγούμενο`,\n next: `Επόμενο`,\n done: `Ολοκληρώθηκε`\n }\n} as FullLocaleData;\n","import { FullLocaleData } from '../locale.types';\n\nexport default {\n abbr: 'en-US',\n exception: {\n 403: `Sorry, you don't have access to this page`,\n 404: `Sorry, the page you visited does not exist`,\n 500: `Sorry, the server is reporting an error`,\n backToHome: 'Back To Home'\n },\n noticeIcon: {\n emptyText: 'No data',\n clearText: 'Clear'\n },\n reuseTab: {\n close: 'Close tab',\n closeOther: 'Close other tabs',\n closeRight: 'Close tabs to right',\n refresh: 'Refresh'\n },\n tagSelect: {\n expand: 'Expand',\n collapse: 'Collapse'\n },\n miniProgress: {\n target: 'Target: '\n },\n st: {\n total: '{{range[0]}} - {{range[1]}} of {{total}}',\n filterConfirm: 'OK',\n filterReset: 'Reset'\n },\n sf: {\n submit: 'Submit',\n reset: 'Reset',\n search: 'Search',\n edit: 'Save',\n addText: 'Add',\n removeText: 'Remove',\n checkAllText: 'Check all',\n error: {\n 'false schema': `Boolean schema is false`,\n $ref: `Can't resolve reference {ref}`,\n additionalItems: `Should not have more than {limit} item`,\n additionalProperties: `Should not have additional properties`,\n anyOf: `Should match some schema in \"anyOf\"`,\n dependencies: `should have property {deps} when property {property} is present`,\n enum: `Should be equal to one of predefined values`,\n format: `Should match format \"{format}\"`,\n type: `Should be {type}`,\n required: `Required`,\n maxLength: `Should not be longer than {limit} character`,\n minLength: `Should not be shorter than {limit} character`,\n minimum: `Should be {comparison} {limit}`,\n formatMinimum: `Should be {comparison} {limit}`,\n maximum: `Should be {comparison} {limit}`,\n formatMaximum: `Should be {comparison} {limit}`,\n maxItems: `Should not have more than {limit} item`,\n minItems: `Should not have less than {limit} item`,\n maxProperties: `Should not have more than {limit} property`,\n minProperties: `Should not have less than {limit} property`,\n multipleOf: `Should be a multiple of {multipleOf}`,\n not: `Should not be valid according to schema in \"not\"`,\n oneOf: `Should match exactly one schema in \"oneOf\"`,\n pattern: `Should match pattern \"{pattern}\"`,\n uniqueItems: `Should not have duplicate items (items ## {j} and {i} are identical)`,\n custom: `Should match format`,\n propertyNames: `Property name \"{propertyName}\" is invalid`,\n patternRequired: `Should have property matching pattern \"{missingPattern}\"`,\n switch: `Should pass \"switch\" keyword validation, case {caseIndex} fails`,\n const: `Should be equal to constant`,\n contains: `Should contain a valid item`,\n formatExclusiveMaximum: `formatExclusiveMaximum should be boolean`,\n formatExclusiveMinimum: `formatExclusiveMinimum should be boolean`,\n if: `Should match \"{failingKeyword}\" schema`\n }\n },\n onboarding: {\n skip: `Skip`,\n prev: `Prev`,\n next: `Next`,\n done: `Done`\n }\n} as FullLocaleData;\n","import { FullLocaleData } from '../locale.types';\n\nexport default {\n abbr: 'es-ES',\n exception: {\n 403: `Lo sentimos, no tiene acceso a esta página`,\n 404: `Lo sentimos, la página que ha visitado no existe`,\n 500: `Lo siento, error interno del servidor `,\n backToHome: 'Volver a la página de inicio'\n },\n noticeIcon: {\n emptyText: 'No hay datos',\n clearText: 'Limpiar'\n },\n reuseTab: {\n close: 'Cerrar pestaña',\n closeOther: 'Cerrar otras pestañas',\n closeRight: 'Cerrar pestañas a la derecha',\n refresh: 'Actualizar'\n },\n tagSelect: {\n expand: 'Expandir',\n collapse: 'Ocultar'\n },\n miniProgress: {\n target: 'Target: '\n },\n st: {\n total: '{{rango[0]}} - {{rango[1]}} de {{total}}',\n filterConfirm: 'Aceptar',\n filterReset: 'Reiniciar'\n },\n sf: {\n submit: 'Submit',\n reset: 'Reiniciar',\n search: 'Buscar',\n edit: 'Guardar',\n addText: 'Añadir',\n removeText: 'Eliminar',\n checkAllText: 'Comprobar todo',\n error: {\n 'false schema': `Boolean schema is false`,\n $ref: `Can't resolve reference {ref}`,\n additionalItems: `Should not have more than {limit} item`,\n additionalProperties: `Should not have additional properties`,\n anyOf: `Should match some schema in \"anyOf\"`,\n dependencies: `should have property {deps} when property {property} is present`,\n enum: `Should be equal to one of predefined values`,\n format: `Should match format \"{format}\"`,\n type: `Should be {type}`,\n required: `Required`,\n maxLength: `Should not be longer than {limit} character`,\n minLength: `Should not be shorter than {limit} character`,\n minimum: `Should be {comparison} {limit}`,\n formatMinimum: `Should be {comparison} {limit}`,\n maximum: `Should be {comparison} {limit}`,\n formatMaximum: `Should be {comparison} {limit}`,\n maxItems: `Should not have more than {limit} item`,\n minItems: `Should not have less than {limit} item`,\n maxProperties: `Should not have more than {limit} property`,\n minProperties: `Should not have less than {limit} property`,\n multipleOf: `Should be a multiple of {multipleOf}`,\n not: `Should not be valid according to schema in \"not\"`,\n oneOf: `Should match exactly one schema in \"oneOf\"`,\n pattern: `Should match pattern \"{pattern}\"`,\n uniqueItems: `Should not have duplicate items (items ## {j} and {i} are identical)`,\n custom: `Should match format`,\n propertyNames: `Property name \"{propertyName}\" is invalid`,\n patternRequired: `Should have property matching pattern \"{missingPattern}\"`,\n switch: `Should pass \"switch\" keyword validation, case {caseIndex} fails`,\n const: `Should be equal to constant`,\n contains: `Should contain a valid item`,\n formatExclusiveMaximum: `formatExclusiveMaximum should be boolean`,\n formatExclusiveMinimum: `formatExclusiveMinimum should be boolean`,\n if: `Should match \"{failingKeyword}\" schema`\n }\n },\n onboarding: {\n skip: `Omitir`,\n prev: `Previo`,\n next: `Siguiente`,\n done: `Terminado`\n }\n} as FullLocaleData;\n","import { FullLocaleData } from '../locale.types';\n\nexport default {\n abbr: 'fr-FR',\n exception: {\n 403: `Désolé, vous n'avez pas accès à cette page`,\n 404: `Désolé, la page que vous avez visitée n'existe pas`,\n 500: `Désolé, le serveur signale une erreur`,\n backToHome: \"Retour à l'accueil\"\n },\n noticeIcon: {\n emptyText: 'Pas de données',\n clearText: 'Effacer'\n },\n reuseTab: {\n close: \"Fermer l'onglet\",\n closeOther: 'Fermer les autres onglets',\n closeRight: 'Fermer les onglets à droite',\n refresh: 'Rafraîchir'\n },\n tagSelect: {\n expand: 'Etendre',\n collapse: 'Effondrer'\n },\n miniProgress: {\n target: 'Cible: '\n },\n st: {\n total: '{{range[0]}} - {{range[1]}} de {{total}}',\n filterConfirm: 'OK',\n filterReset: 'Réinitialiser'\n },\n sf: {\n submit: 'Soumettre',\n reset: 'Réinitialiser',\n search: 'Rechercher',\n edit: 'Sauvegarder',\n addText: 'Ajouter',\n removeText: 'Supprimer',\n checkAllText: 'Cochez toutes',\n error: {\n 'false schema': `Boolean schema is false`,\n $ref: `Can't resolve reference {ref}`,\n additionalItems: `Should not have more than {limit} item`,\n additionalProperties: `Should not have additional properties`,\n anyOf: `Should match some schema in \"anyOf\"`,\n dependencies: `should have property {deps} when property {property} is present`,\n enum: `Should be equal to one of predefined values`,\n format: `Should match format \"{format}\"`,\n type: `Should be {type}`,\n required: `Required`,\n maxLength: `Should not be longer than {limit} character`,\n minLength: `Should not be shorter than {limit} character`,\n minimum: `Should be {comparison} {limit}`,\n formatMinimum: `Should be {comparison} {limit}`,\n maximum: `Should be {comparison} {limit}`,\n formatMaximum: `Should be {comparison} {limit}`,\n maxItems: `Should not have more than {limit} item`,\n minItems: `Should not have less than {limit} item`,\n maxProperties: `Should not have more than {limit} property`,\n minProperties: `Should not have less than {limit} property`,\n multipleOf: `Should be a multiple of {multipleOf}`,\n not: `Should not be valid according to schema in \"not\"`,\n oneOf: `Should match exactly one schema in \"oneOf\"`,\n pattern: `Should match pattern \"{pattern}\"`,\n uniqueItems: `Should not have duplicate items (items ## {j} and {i} are identical)`,\n custom: `Should match format`,\n propertyNames: `Property name \"{propertyName}\" is invalid`,\n patternRequired: `Should have property matching pattern \"{missingPattern}\"`,\n switch: `Should pass \"switch\" keyword validation, case {caseIndex} fails`,\n const: `Should be equal to constant`,\n contains: `Should contain a valid item`,\n formatExclusiveMaximum: `formatExclusiveMaximum should be boolean`,\n formatExclusiveMinimum: `formatExclusiveMinimum should be boolean`,\n if: `Should match \"{failingKeyword}\" schema`\n }\n },\n onboarding: {\n skip: `Passer`,\n prev: `Précédent`,\n next: `Suivant`,\n done: `Terminé`\n }\n} as FullLocaleData;\n","import { FullLocaleData } from '../locale.types';\n\nexport default {\n abbr: 'hr-HR',\n exception: {\n 403: `Nažalost, nemate pristup ovoj lokaciji`,\n 404: `Nažalost, lokacija ne postoji`,\n 500: `Nažalost, server je javio pogrešku`,\n backToHome: 'Nazad na početnu stranicu'\n },\n noticeIcon: {\n emptyText: 'Nema podataka',\n clearText: 'Obriši'\n },\n reuseTab: {\n close: 'Zatvori karticu',\n closeOther: 'Zatvori druge kartice',\n closeRight: 'Zatvori kartice desno',\n refresh: 'Refresh'\n },\n tagSelect: {\n expand: 'Proširi',\n collapse: 'Skupi'\n },\n miniProgress: {\n target: 'Cilj: '\n },\n st: {\n total: '{{range[0]}} - {{range[1]}} od {{total}}',\n filterConfirm: 'U redu',\n filterReset: 'Poništi'\n },\n sf: {\n submit: 'Pošalji',\n reset: 'Poništi',\n search: 'Pretraži',\n edit: 'Spremi',\n addText: 'Dodaj',\n removeText: 'Ukloni',\n checkAllText: 'Označi sve'\n },\n onboarding: {\n skip: `Preskočiti`,\n prev: `Prethodna`,\n next: `Sljedeći`,\n done: `Sastavljeno`\n }\n} as FullLocaleData;\n","import { FullLocaleData } from '../locale.types';\n\nexport default {\n abbr: 'it-IT',\n exception: {\n 403: `Spiacenti, non hai accesso a questa pagina`,\n 404: `Spiacenti, la pagina che hai visitato non esiste`,\n 500: `Spiacenti, il server sta riscontrando un errore`,\n backToHome: 'Torna alla Home'\n },\n noticeIcon: {\n emptyText: 'Nessun dato',\n clearText: 'Cancella memoria locale'\n },\n reuseTab: {\n close: 'Chiudi la scheda',\n closeOther: 'Chiudi le altre schede',\n closeRight: 'Chiudi le schede a destra',\n refresh: 'Aggiorna'\n },\n tagSelect: {\n expand: 'Espandi',\n collapse: 'Comprimi'\n },\n miniProgress: {\n target: 'Obiettivo: '\n },\n st: {\n total: '{{range[0]}} - {{range[1]}} di {{total}}',\n filterConfirm: 'OK',\n filterReset: 'Reimposta'\n },\n sf: {\n submit: 'Invia',\n reset: 'Reimposta',\n search: 'Cerca',\n edit: 'Salva',\n addText: 'Aggiungi',\n removeText: 'Rimuovi',\n checkAllText: 'Seleziona tutto',\n error: {\n 'false schema': `Lo schema booleano è falso`,\n $ref: `Impossibile risolvere il riferimento {ref}`,\n additionalItems: `Non deve avere più di {limit} elementi`,\n additionalProperties: `Non deve avere proprietà aggiuntive`,\n anyOf: `Deve corrispondere a uno schema in \"anyOf\"`,\n dependencies: `Deve avere una proprietà {deps} quando è presente la proprietà {property}`,\n enum: `Deve essere uguale a uno dei valori predefiniti`,\n format: `Deve corrispondere al formato \"{format}\"`,\n type: `Deve essere {type}`,\n required: `Obbligatorio`,\n maxLength: `Non deve essere superiore a {limit} caratteri`,\n minLength: `Non deve essere superiore a {limit} caratteri`,\n minimum: `Deve essere {comparison} {limit}`,\n formatMinimum: `Deve essere {comparison} {limit}`,\n maximum: `Deve essere {comparison} {limit}`,\n formatMaximum: `Deve essere {comparison} {limit}`,\n maxItems: `Non deve avere più di {limit} elementi`,\n minItems: `Non deve avere meno di {limit} elementi`,\n maxProperties: `Non deve avere più di {limit} proprietà`,\n minProperties: `Non deve avere meno di {limit} proprietà`,\n multipleOf: `Deve essere un multiplo di {multipleOf}`,\n not: