Merge pull request #130 in YR/star-web-components from l10n to master

* commit '722062d545e8435e63b028727e7c22ff223c9f17':
  TASK: #103629 - (star-web-elements) 添加国际化帮助类
  TASK: #103629 - 组件增添国际化功能
This commit is contained in:
汪昌棋 2022-11-10 09:16:52 +08:00
commit 8b0e43bfc2
3 changed files with 224 additions and 1 deletions

View File

@ -0,0 +1,179 @@
import {StarBaseElement} from './star-base-element'
interface PropertiesParser {
patterns: {[type: string]: RegExp}
entryIds: object
init: () => void
parse: (ctx: any, source: string) => any[]
parseEntity: (id: string, value: string, ast: string[]) => void
setEntityValue: (
id: string,
attr: string,
key: string,
rawValue: string,
ast: any[]
) => any
parseString: (str: string) => any[]
unescapeString: (str: string) => string
parseIndex: (str: string) => (string | {t: string; v: string})[]
}
class L10nHelper {
/* 开启了国际化功能的组件 */
updateArray: Set<StarBaseElement> = new Set()
// TODO: fluent.js 替换完后更改此处
l10n: any
/* 国际化资源转化器 */
PropertiesParser?: PropertiesParser
suspending: Function[] = []
constructor() {
this.init()
}
init = (): void => {
// @ts-ignore
const l10n = navigator.mozL10n
if (!l10n) {
return document.addEventListener('readystatechange', this.init, {
once: true,
})
}
this.l10n = l10n
/* 变更语言时, 刷新组件 */
window.addEventListener('languagechange', () => {
this.updateArray.forEach((element) => element.requestUpdate())
})
if (this.suspending.length) {
this.suspending.forEach((fun) => fun())
}
this.loadAllLocales()
}
/* 空闲时加载所有国际化资源, 防止语言切换时闪烁 */
loadAllLocales = () => {
requestIdleCallback(() => {
const availableLocales: string[] = this.l10n.ctx.availableLocales
const shouldRequest: string[] = []
availableLocales.forEach((lang) => {
const locale = this.l10n.ctx.getLocale(lang)
if (!locale.isReady) {
shouldRequest.push(lang)
}
})
shouldRequest.length && this.l10n.ctx.requestLocales(...shouldRequest)
})
}
observe = (el: StarBaseElement) => {
this.updateArray.add(el)
}
unobserve = (el: StarBaseElement) => {
this.updateArray.delete(el)
}
/* 添加国际化资源 */
addLocaleSrc = (json: {[lang: string]: {[l10nId: string]: string}}): void => {
if (!this.l10n || !this.l10n.ctx.isReady) {
this.suspending.push(this.addLocaleSrc.bind(this, json))
return
}
this.initParser().then(() => {
for (const language in json) {
const context = json[language]
const ast = this.parse(context)
const locale = this.l10n.ctx.getLocale(language)
locale.addAST(ast)
}
})
}
/* 初始化转化器 */
initParser = () => {
if (this.l10n.ctx.isReady && this.PropertiesParser) {
return Promise.resolve()
}
return new Promise((res) => {
this.l10n.once(() => {
this.PropertiesParser = this.l10n._getInternalAPI()
.PropertiesParser as PropertiesParser
this.PropertiesParser.init()
res(void 0)
})
})
}
parse = (context: {[l10nId: string]: string}) => {
let str = ''
for (const key in context) {
const value = context[key]
str += `${key}=${value}\n`
}
return this.PropertiesParser!.parse(null, str)
}
get = (id: string, ctxdata: any) => {
let result = ''
if (this.l10n) {
if (this.l10n.ctx.isReady) {
// 如果L10n已经准备好, 则获取对应国际化结果
if (this.l10n.ctx.getLocale(navigator.language).isReady) {
result = this.l10n.get(id, ctxdata)
if (!result) {
console.warn(
'l10n get nothing by id:',
id,
'; and ctxdata:',
ctxdata
)
}
} else {
// 相应的国际化资源未准备完成, 待完成后重新刷新
this.openLocaleListener()
}
} else {
// L10n还没有准备好, 等待L10n准备完毕后, 刷新小组件
this.openL10nReadyListener()
}
} else {
console.warn('l10n is not exist!')
}
return result
}
/* 等待国际化资源加载, 添加加载成功的事件监听 */
_localeListening: boolean = false
openLocaleListener = () => {
if (!this._localeListening) {
this._localeListening = true
const cb = () => {
this.updateArray.forEach((el) => el.requestUpdate())
this.l10n.ctx.removeEventListener('request-locales-done', cb)
this._localeListening = false
}
this.l10n.ctx.addEventListener('request-locales-done', cb)
}
}
/* 等待L10n准备完毕, 添加准备完毕的事件监听 */
_readyListening: boolean = false
openL10nReadyListener = () => {
if (!this._readyListening) {
this._readyListening = true
this.l10n.once(() => {
this.updateArray.forEach((el) => el.requestUpdate())
this._readyListening = false
})
}
}
}
export const l10nHelper = new L10nHelper()

View File

@ -5,6 +5,7 @@ import GestureDetector, {
} from '../../lib/gesture/gesture-detector'
import {autoPxStyle} from './auto-px-style'
import {globalStyles} from './global-style'
import {l10nHelper} from './custome_element_l10n'
declare global {
var loadStarMixin: boolean
@ -113,6 +114,16 @@ export type StarElementEventMap = HTMLElementEventMap & GestureEvents
export class StarBaseElement extends StarMixin(LitElement) {
gestureDetector!: GestureDetector
disconnectedCallback(): void {
super.disconnectedCallback()
this._l10n && l10nHelper.observe(this)
}
connectedCallback(): void {
super.connectedCallback()
this._l10n && l10nHelper.unobserve(this)
}
/**
*
*/
@ -126,6 +137,35 @@ export class StarBaseElement extends StarMixin(LitElement) {
GestureDetector.disembedded(this)
}
/**
*
*/
addLocaleSrc(json: {[lang: string]: {[l10nId: string]: string}}): void {
l10nHelper.addLocaleSrc(json)
}
/* 该组件是否启用了国际化功能, 用于判断是否因系统语言变化而刷新 */
_languageChangeObserve: boolean = false
get _l10n() {
return this._languageChangeObserve
}
set _l10n(value) {
if (value) {
if (!this._languageChangeObserve) {
l10nHelper.observe(this)
}
} else {
l10nHelper.unobserve(this)
}
this._languageChangeObserve = value
}
/* 根据 id 和传值 ctxdata 获取国际化结果 */
$l = (id: string, ctxdata?: string) => {
this._l10n = true
return l10nHelper.get(id, ctxdata)
}
public static get styles(): CSSResultArray {
return []
}

View File

@ -4,5 +4,9 @@
"composite": true,
"rootDir": "../../"
},
"include": ["*.ts", "../../lib/gesture/gesture-detector.ts"]
"include": [
"*.ts",
"../../lib/gesture/gesture-detector.ts",
"../../../../typings/"
]
}