diff --git a/packages/gem-examples/src/i18n/index.ts b/packages/gem-examples/src/i18n/index.ts index 3411dfee..cbf19ebb 100644 --- a/packages/gem-examples/src/i18n/index.ts +++ b/packages/gem-examples/src/i18n/index.ts @@ -1,6 +1,8 @@ import { GemElement, html, connectStore, customElement, render } from '@mantou/gem'; import { I18n } from '@mantou/gem/helper/i18n'; +import type { RouteItem } from 'duoyun-ui/elements/route'; +import 'duoyun-ui/elements/route'; import '../elements/layout'; const en = { @@ -29,6 +31,27 @@ const i18nModule = i18n.createSubModule('test', { zh: 'data:text/plain;base64,eyJ0aXRsZSI6Iui/meaYr0kxOG4ifQ==', }); +const localizeRoutes: RouteItem[] = [ + { + pattern: 'en', + getContent() { + return html`en element`; + }, + }, + { + pattern: 'de', + getContent() { + return html`de element`; + }, + }, + { + pattern: '*', + getContent() { + return html`other element`; + }, + }, +]; + @connectStore(i18n.store) @connectStore(i18nModule.store) @customElement('app-root') @@ -48,6 +71,9 @@ export class App extends GemElement {

${i18nModule.get('title')}

${i18nModule.get('hello', 'World', 'reverse')}

${i18nModule.get('detail', (s) => html`${s}`)}

+ +

localize content

+ `; } } diff --git a/packages/gem/docs/en/001-guide/002-advance/005-i18n.md b/packages/gem/docs/en/001-guide/002-advance/005-i18n.md index ee897038..c13cf3b0 100644 --- a/packages/gem/docs/en/001-guide/002-advance/005-i18n.md +++ b/packages/gem/docs/en/001-guide/002-advance/005-i18n.md @@ -77,7 +77,7 @@ const i18n = new I18n({ }, }); -html`Hello, ${i18n.get('hello', 'World', 'reverse')}`; +html`${i18n.get('hello', 'World', 'reverse')}`; // Hello, World! reverse=> World, Hello! ``` @@ -94,7 +94,7 @@ const i18n = new I18n({ }, }); -html`Hello, ${i18n.get('detail', (s) => html`${s}`)}`; +html`${i18n.get('detail', (s) => html`${s}`)}`; ``` ## Language specified in URL @@ -147,6 +147,24 @@ const i18n = new I18n({ `I18n.createSubModule` creates sub-modules, you can split language packages by route to speed up the rendering of the first screen. +## Localize + +You can use `` to render different content according to different languages. You only need to specify the `trigger` to `i18n`: + +```js +const localizeRoutes = [ + { + pattern: 'zh-*', + async getContent() { + import('...'); + return html`other element`; + }, + }, +]; + +html``; +``` + ## Example diff --git a/packages/gem/docs/zh/001-guide/002-advance/005-i18n.md b/packages/gem/docs/zh/001-guide/002-advance/005-i18n.md index d0489688..3c526893 100644 --- a/packages/gem/docs/zh/001-guide/002-advance/005-i18n.md +++ b/packages/gem/docs/zh/001-guide/002-advance/005-i18n.md @@ -77,7 +77,7 @@ const i18n = new I18n({ }, }); -html`Hello, ${i18n.get('hello', 'World', 'reverse')}`; +html`${i18n.get('hello', 'World', 'reverse')}`; // Hello, World! reverse=> World, Hello! ``` @@ -94,7 +94,7 @@ const i18n = new I18n({ }, }); -html`Hello, ${i18n.get('detail', (s) => html`${s}`)}`; +html`${i18n.get('detail', (s) => html`${s}`)}`; ``` ## URL 中指定语言 @@ -145,7 +145,25 @@ const i18n = new I18n({ ## 子模块 -`I18n.createSubModule` 创建子模块,你可以按路由分割语言包,加快首屏渲染速度 +`I18n.createSubModule` 创建子模块,你可以按路由分割语言包,加快首屏渲染速度。 + +## 本地化 + +可以使用 `` 根据不同语言渲染不同的内容,只需要将 `trigger` 指定为 `i18n` 并即可,例如: + +```js +const localizeRoutes = [ + { + pattern: 'zh-*', + async getContent() { + import('...'); + return html`other element`; + }, + }, +]; + +html``; +``` ## 例子 diff --git a/packages/gem/src/elements/base/route.ts b/packages/gem/src/elements/base/route.ts index e6b2b6fe..6a51d554 100644 --- a/packages/gem/src/elements/base/route.ts +++ b/packages/gem/src/elements/base/route.ts @@ -1,6 +1,6 @@ import { GemElement, html, TemplateResult } from '../../lib/element'; -import { property, connectStore, emitter, Emitter, boolattribute } from '../../lib/decorators'; -import { createStore, updateStore, Store } from '../../lib/store'; +import { property, emitter, Emitter, boolattribute } from '../../lib/decorators'; +import { createStore, updateStore, Store, connect } from '../../lib/store'; import { titleStore, history, UpdateHistoryParams } from '../../lib/history'; import { QueryString } from '../../lib/utils'; @@ -111,13 +111,18 @@ type State = { content?: TemplateResult; }; +export type RouteTrigger = { + store: Store; + replace: (arg: { path: string }) => void; + getParams: () => { path: string; query: any }; +}; + /** * @attr inert 暂停路由更新 * @fires routechange * @fires error * @fires loading */ -@connectStore(history.store) export class GemRouteElement extends GemElement { @boolattribute transition: boolean; @property routes?: RouteItem[] | RoutesObject; @@ -134,6 +139,8 @@ export class GemRouteElement extends GemElement { @emitter loading: Emitter; @emitter error: Emitter; + @property trigger: RouteTrigger = history; + /**当前使用的路由对象 */ currentRoute: RouteItem | null; /**当前匹配的路由的 params */ @@ -209,6 +216,12 @@ export class GemRouteElement extends GemElement { }; mounted() { + this.effect( + // 触发 this.#update + () => connect(this.trigger.store, () => this.setState({})), + () => [this.trigger], + ); + this.effect( ([key, path], old) => { // 只有查询参数改变 @@ -218,7 +231,10 @@ export class GemRouteElement extends GemElement { } this.update(); }, - () => [this.key, history.getParams().path, location.search], + () => { + const { path, query } = this.trigger.getParams(); + return [this.key, path + query]; + }, ); } @@ -243,13 +259,15 @@ export class GemRouteElement extends GemElement { } update = () => { - const { route, params = {} } = GemRouteElement.findRoute(this.routes, history.getParams().path); + const { route, params = {} } = GemRouteElement.findRoute(this.routes, this.trigger.getParams().path); const { redirect, content, getContent } = route || {}; if (redirect) { - history.replace({ path: redirect }); + this.trigger.replace({ path: redirect }); return; } - updateStore(titleStore, { title: route?.title }); + if (this.trigger === history) { + updateStore(titleStore, { title: route?.title }); + } const contentOrLoader = content || getContent?.(params); if (contentOrLoader instanceof Promise) { this.loading(route!); diff --git a/packages/gem/src/helper/i18n.ts b/packages/gem/src/helper/i18n.ts index 8059084f..266bb710 100644 --- a/packages/gem/src/helper/i18n.ts +++ b/packages/gem/src/helper/i18n.ts @@ -1,6 +1,7 @@ import { createStore, updateStore, Store } from '../lib/store'; import { html, TemplateResult } from '../lib/element'; import { GemError } from '../lib/utils'; +import type { RouteTrigger } from '../elements/base/route'; import { logger } from './logger'; @@ -61,7 +62,7 @@ interface I18nOptions { onChange?: (currentLanguage: string) => void; } -export class I18n> { +export class I18n> implements RouteTrigger { resources: Resources; fallbackLanguage: string; currentLanguage: string; @@ -73,6 +74,12 @@ export class I18n> { store: Store; + replace = ({ path }: { path: string }) => { + this.setLanguage(path); + }; + + getParams = () => ({ path: this.currentLanguage, query: '' }); + get #cacheCurrentKey() { return `${this.cachePrefix}:current`; } diff --git a/packages/gem/src/lib/element.ts b/packages/gem/src/lib/element.ts index 2c1f5d89..3c93e1de 100644 --- a/packages/gem/src/lib/element.ts +++ b/packages/gem/src/lib/element.ts @@ -276,7 +276,8 @@ export abstract class GemElement> extends HTMLElemen /** * @helper - * 在 `render` 前执行回调,和 `effect` 一样接受依赖数组参数,在 `constructor`/`willMount` 中使用; + * 在 `render` 前执行回调,不要在里面使用 `setState` + * 和 `effect` 一样接受依赖数组参数,在 `constructor`/`willMount` 中使用; * 第一次执行时 `oldDeps` 为空 * * ```js