feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
17
frontend/packages/arch/i18n/src/global.d.ts
vendored
Normal file
17
frontend/packages/arch/i18n/src/global.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/// <reference types='@coze-arch/bot-typings' />
|
||||
29
frontend/packages/arch/i18n/src/i18n-provider/context.tsx
Normal file
29
frontend/packages/arch/i18n/src/i18n-provider/context.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { type Intl } from '../intl';
|
||||
|
||||
interface I18nContext {
|
||||
i18n: Intl;
|
||||
}
|
||||
const i18nContext = React.createContext<I18nContext>({
|
||||
i18n: {
|
||||
t: k => k,
|
||||
} as unknown as Intl,
|
||||
});
|
||||
export { i18nContext, type I18nContext };
|
||||
52
frontend/packages/arch/i18n/src/i18n-provider/index.tsx
Normal file
52
frontend/packages/arch/i18n/src/i18n-provider/index.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, type ReactNode } from 'react';
|
||||
|
||||
import { CDLocaleProvider } from '@coze-arch/coze-design/locales';
|
||||
|
||||
import { type Intl } from '../intl';
|
||||
import { i18nContext, type I18nContext } from './context';
|
||||
|
||||
export { i18nContext, type I18nContext };
|
||||
|
||||
export interface I18nProviderProps {
|
||||
children?: ReactNode;
|
||||
i18n: Intl;
|
||||
}
|
||||
|
||||
export class I18nProvider extends Component<I18nProviderProps> {
|
||||
constructor(props: I18nProviderProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
children,
|
||||
i18n = {
|
||||
t: (k: string) => k,
|
||||
},
|
||||
} = this.props;
|
||||
return (
|
||||
<CDLocaleProvider i18n={i18n}>
|
||||
<i18nContext.Provider value={{ i18n: i18n as Intl }}>
|
||||
{children}
|
||||
</i18nContext.Provider>
|
||||
</CDLocaleProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
116
frontend/packages/arch/i18n/src/index.ts
Normal file
116
frontend/packages/arch/i18n/src/index.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
/* eslint-disable max-params */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type {
|
||||
LocaleData,
|
||||
I18nOptionsMap,
|
||||
I18nKeysHasOptionsType,
|
||||
I18nKeysNoOptionsType,
|
||||
} from '@coze-studio/studio-i18n-resource-adapter';
|
||||
|
||||
import {
|
||||
type Intl,
|
||||
type I18nCore,
|
||||
type IIntlInitOptions,
|
||||
I18n as _I18n,
|
||||
} from './intl';
|
||||
|
||||
type Callback = Parameters<(typeof _I18n)['init']>[1];
|
||||
type FallbackLng = ReturnType<(typeof _I18n)['getLanguages']>;
|
||||
type IntlModule = Parameters<(typeof _I18n)['use']>[0];
|
||||
type InitReturnType = ReturnType<(typeof _I18n)['init']>;
|
||||
type I18nOptions<K extends LocaleData> = K extends keyof I18nOptionsMap
|
||||
? I18nOptionsMap[K]
|
||||
: never;
|
||||
|
||||
// 这里导出的 const I18n = new FlowIntl() 与 '@edenx/plugin-starling-intl/runtime' 中的 I18n 功能等价
|
||||
// 其实就是对 '@edenx/plugin-starling-intl/runtime' 中的 I18n 进行了一层封装,目的是为了后续进一步灵活的定义I18n.t() 的参数类型。
|
||||
// 这里的 I18n.t() 的参数类型是通过泛型 LocaleData 来定义的,而 '@edenx/plugin-starling-intl/runtime' 中的 I18n.t() 的参数类型是通过泛型 string 来定义的。
|
||||
class FlowIntl {
|
||||
plugins: any[] = [];
|
||||
public i18nInstance: I18nCore;
|
||||
constructor() {
|
||||
this.i18nInstance = _I18n.i18nInstance;
|
||||
}
|
||||
|
||||
init(config: IIntlInitOptions, callback?: Callback): InitReturnType {
|
||||
return _I18n.init(config, callback);
|
||||
}
|
||||
|
||||
use(plugin: IntlModule): Intl {
|
||||
return _I18n.use(plugin);
|
||||
}
|
||||
|
||||
get language(): string {
|
||||
return _I18n.language;
|
||||
}
|
||||
|
||||
setLangWithPromise(lng: string) {
|
||||
return this.i18nInstance.changeLanguageWithPromise(lng);
|
||||
}
|
||||
|
||||
setLang(lng: string, callback?: Callback): void {
|
||||
return _I18n.setLang(lng, callback);
|
||||
}
|
||||
|
||||
getLanguages(): FallbackLng {
|
||||
return _I18n.getLanguages();
|
||||
}
|
||||
|
||||
dir(): 'ltr' | 'rtl' {
|
||||
return _I18n.dir();
|
||||
}
|
||||
|
||||
addResourceBundle(
|
||||
lng: string,
|
||||
ns: string,
|
||||
resources: any,
|
||||
deep?: boolean,
|
||||
overwrite?: boolean,
|
||||
) {
|
||||
return _I18n.addResourceBundle(lng, ns, resources, deep, overwrite);
|
||||
}
|
||||
|
||||
t<K extends I18nKeysNoOptionsType>(
|
||||
keys: K,
|
||||
// 这里如果用 never 的话,导致存量代码第二个参数是 `{}` 的时候会报错,所以这里用 Record<string, unknown> 代替
|
||||
// 后续的做法是:用 sg 把存量的代码都修复了之后,这里再改成 never 类型,从而保证未来新增的代码,都是有类型检查的。
|
||||
// 记得改动的时候 #87 行也要一起修改
|
||||
options?: Record<string, unknown>,
|
||||
fallbackText?: string,
|
||||
): string;
|
||||
t<K extends I18nKeysHasOptionsType>(
|
||||
keys: K,
|
||||
options: I18nOptions<K>,
|
||||
fallbackText?: string,
|
||||
): string;
|
||||
t<K extends LocaleData>(
|
||||
keys: K,
|
||||
options?: I18nOptions<K> | Record<string, unknown>,
|
||||
fallbackText?: string,
|
||||
): string {
|
||||
// tecvan: fixme, hard to understand why this happens
|
||||
return _I18n.t(keys, options, fallbackText);
|
||||
}
|
||||
}
|
||||
|
||||
export const getUnReactiveLanguage = () => _I18n.language;
|
||||
export const I18n = new FlowIntl();
|
||||
|
||||
export { type I18nKeysNoOptionsType, type I18nKeysHasOptionsType };
|
||||
134
frontend/packages/arch/i18n/src/intl/i18n-impl.ts
Normal file
134
frontend/packages/arch/i18n/src/intl/i18n-impl.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-params */
|
||||
/* eslint-disable @typescript-eslint/no-this-alias */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type { Callback, TFunction, InitOptions, FallbackLng } from 'i18next';
|
||||
|
||||
import type { StringMap, TFunctionKeys } from './types';
|
||||
import I18next, { formatLang, isTypes, LANGUAGE_TRANSFORMER } from './i18n';
|
||||
|
||||
export interface IntlConstructorOptions {
|
||||
i18nInstance?: I18next;
|
||||
}
|
||||
let intlInstance: any = null;
|
||||
/**
|
||||
* I18n实例
|
||||
* 自定义配置
|
||||
*/
|
||||
class Intl {
|
||||
plugins: any[];
|
||||
i18nInstance: I18next;
|
||||
constructor(opts?: IntlConstructorOptions) {
|
||||
this.plugins = [];
|
||||
this.i18nInstance = opts?.i18nInstance ?? new I18next();
|
||||
}
|
||||
/**
|
||||
* i18n 没有定义类型,这里声明 any
|
||||
*/
|
||||
use(plugin: any) {
|
||||
if (!this.plugins.includes(plugin)) {
|
||||
this.plugins.push(plugin);
|
||||
return this;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
async init(
|
||||
config: InitOptions,
|
||||
initCallback?: Callback,
|
||||
): Promise<{ err: Error; t: TFunction }> {
|
||||
this.i18nInstance._handleConfigs(config as any);
|
||||
this.i18nInstance._handlePlugins(this.plugins);
|
||||
|
||||
try {
|
||||
const { err, t } = await this.i18nInstance.createInstance();
|
||||
|
||||
typeof initCallback === 'function' && initCallback(err, t);
|
||||
return { err, t };
|
||||
} catch (err) {
|
||||
console.log('debugger error', err);
|
||||
return {
|
||||
err,
|
||||
t: ((key: string) => key) as TFunction<'translation', undefined>,
|
||||
};
|
||||
}
|
||||
}
|
||||
get language() {
|
||||
return (this.i18nInstance || {}).language;
|
||||
}
|
||||
getLanguages(): FallbackLng {
|
||||
return this.i18nInstance.getLanguages() || [];
|
||||
}
|
||||
setLang(lng: string, callback?: Callback) {
|
||||
const formatLng = formatLang(
|
||||
lng,
|
||||
this.plugins.filter(isTypes(LANGUAGE_TRANSFORMER)),
|
||||
);
|
||||
this.i18nInstance.changeLanguage(formatLng, callback);
|
||||
}
|
||||
setLangWithPromise(lng: string) {
|
||||
const formatLng = formatLang(
|
||||
lng,
|
||||
this.plugins.filter(isTypes(LANGUAGE_TRANSFORMER)),
|
||||
);
|
||||
return this.i18nInstance.changeLanguageWithPromise(formatLng);
|
||||
}
|
||||
dir(lng: string) {
|
||||
return this.i18nInstance.getDir(lng);
|
||||
}
|
||||
addResourceBundle(
|
||||
lng: string,
|
||||
ns: string,
|
||||
resources: any,
|
||||
deep?: boolean,
|
||||
overwrite?: boolean,
|
||||
) {
|
||||
// to to something validate
|
||||
return this.i18nInstance.addResourceBundle(
|
||||
lng,
|
||||
ns,
|
||||
resources,
|
||||
deep,
|
||||
overwrite,
|
||||
);
|
||||
}
|
||||
t<
|
||||
TKeys extends TFunctionKeys = string,
|
||||
TInterpolationMap extends object = StringMap,
|
||||
>(keys: TKeys | TKeys[], options?: TInterpolationMap, fallbackText?: string) {
|
||||
let that: any = null;
|
||||
if (typeof this === 'undefined') {
|
||||
that = intlInstance;
|
||||
} else {
|
||||
that = this;
|
||||
}
|
||||
if (!that.i18nInstance || !that.i18nInstance.init) {
|
||||
return fallbackText ?? (Array.isArray(keys) ? keys[0] : keys);
|
||||
}
|
||||
// 有人给 key 传空字符串?
|
||||
if (!keys || (typeof keys === 'string' && !keys.trim())) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return that.i18nInstance.t(keys, options, fallbackText);
|
||||
}
|
||||
}
|
||||
|
||||
intlInstance = new Intl();
|
||||
|
||||
export default Intl;
|
||||
export { intlInstance as IntlInstance };
|
||||
259
frontend/packages/arch/i18n/src/intl/i18n.ts
Normal file
259
frontend/packages/arch/i18n/src/intl/i18n.ts
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-params */
|
||||
/* eslint-disable no-empty */
|
||||
/* eslint-disable @coze-arch/no-empty-catch */
|
||||
/* eslint-disable @coze-arch/use-error-in-catch */
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import ICU from 'i18next-icu';
|
||||
import i18next, {
|
||||
type TOptions,
|
||||
type Callback,
|
||||
type FallbackLng,
|
||||
type InitOptions,
|
||||
type Module,
|
||||
type TFunction,
|
||||
type i18n,
|
||||
} from 'i18next';
|
||||
|
||||
import {
|
||||
type StringMap,
|
||||
type TFunctionKeys,
|
||||
type TFunctionResult,
|
||||
} from './types';
|
||||
|
||||
export const LANGUAGE_TRANSFORMER = 'languageTransformer';
|
||||
|
||||
export function isTypes(type) {
|
||||
return itm => itm.type === type;
|
||||
}
|
||||
|
||||
export function formatLang(lng, plugins) {
|
||||
let fl = lng;
|
||||
(plugins || []).map(plugin => {
|
||||
fl = plugin.process(lng) || fl;
|
||||
});
|
||||
return fl;
|
||||
}
|
||||
|
||||
const defaultFallbackLanguage = 'zh-CN';
|
||||
const defaultConfig = {
|
||||
lng: defaultFallbackLanguage, // 如果使用了 Language Detector,i18next 底层 lng 的权重是大于插件的
|
||||
fallbackLng: ['en-US'],
|
||||
inContext: true,
|
||||
};
|
||||
// 默认开启ICU插值解析
|
||||
|
||||
/**
|
||||
* I18n内核
|
||||
* 安全校验
|
||||
*/
|
||||
export default class I18next {
|
||||
instance: i18n;
|
||||
config?: InitOptions & {
|
||||
lng?: string;
|
||||
fallbackLng?: string[];
|
||||
[key: string]: any;
|
||||
};
|
||||
plugins?: any[];
|
||||
languages?: FallbackLng;
|
||||
init?: boolean;
|
||||
userLng?: string | null;
|
||||
|
||||
private _waitingToAddResourceBundle: [
|
||||
string,
|
||||
string,
|
||||
any,
|
||||
boolean,
|
||||
boolean,
|
||||
][] = [];
|
||||
|
||||
_handlePlugins(plugins?: any[]) {
|
||||
this.plugins = plugins;
|
||||
}
|
||||
|
||||
_handleConfigs(config?: InitOptions) {
|
||||
this.userLng = config?.lng || null; // 用户自己设定的 lng
|
||||
|
||||
this.config = Object.assign({}, defaultConfig, config || {});
|
||||
}
|
||||
|
||||
constructor(
|
||||
config?: InitOptions & { copiedI18nextInstance?: any },
|
||||
plugins?: any[],
|
||||
) {
|
||||
if (config?.copiedI18nextInstance) {
|
||||
// just clone instance
|
||||
this.instance = config.copiedI18nextInstance;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this._handlePlugins(plugins);
|
||||
this._handleConfigs(config);
|
||||
this.instance = i18next.createInstance();
|
||||
this.instance.use(ICU);
|
||||
this.instance.isInitialized = false;
|
||||
}
|
||||
get language() {
|
||||
return (this.instance || {}).language;
|
||||
}
|
||||
createInstance(): Promise<{ err: Error; t: TFunction }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.plugins?.map(p => {
|
||||
this.instance.use(p as Module);
|
||||
});
|
||||
|
||||
const { config } = this;
|
||||
|
||||
this.config!.formats = Object.assign({}, this.config!.formats);
|
||||
const formatLng = formatLang(
|
||||
config!.lng,
|
||||
this.plugins?.filter(isTypes(LANGUAGE_TRANSFORMER)),
|
||||
);
|
||||
this.instance.init(
|
||||
{
|
||||
...config,
|
||||
lng: formatLng,
|
||||
i18nFormat: {
|
||||
...(config!.i18nFormat || {}),
|
||||
formats: this.config!.formats,
|
||||
},
|
||||
},
|
||||
(err, t) => {
|
||||
// 初始化好了
|
||||
|
||||
try {
|
||||
// 把等待添加的东西都加进去
|
||||
for (const item of this._waitingToAddResourceBundle) {
|
||||
this.instance.addResourceBundle(...item);
|
||||
}
|
||||
this._waitingToAddResourceBundle = [];
|
||||
} catch (_err) {}
|
||||
|
||||
if (!err) {
|
||||
this._updateLanguages();
|
||||
resolve({
|
||||
t,
|
||||
err,
|
||||
});
|
||||
}
|
||||
this.init = true;
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
reject({
|
||||
t,
|
||||
err,
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
getLanguages() {
|
||||
return this.languages;
|
||||
}
|
||||
addResourceBundle(
|
||||
lng: string,
|
||||
ns: string,
|
||||
resources: any,
|
||||
deep?: boolean,
|
||||
overwrite?: boolean,
|
||||
) {
|
||||
if (this.instance.isInitialized) {
|
||||
return this.instance.addResourceBundle(
|
||||
lng,
|
||||
ns,
|
||||
resources,
|
||||
deep,
|
||||
overwrite,
|
||||
);
|
||||
}
|
||||
// 还没初始化好
|
||||
this._waitingToAddResourceBundle.push([
|
||||
lng,
|
||||
ns,
|
||||
resources,
|
||||
!!deep,
|
||||
!!overwrite,
|
||||
]);
|
||||
return this.instance;
|
||||
}
|
||||
_updateLanguages() {
|
||||
this.languages = this.instance
|
||||
? (Array.from(
|
||||
new Set([this.instance.language, ...this.instance.languages]),
|
||||
) as FallbackLng)
|
||||
: (null as unknown as FallbackLng);
|
||||
}
|
||||
changeLanguage(lng: string, callback?: Callback) {
|
||||
this.config!.lng = lng;
|
||||
this.instance.changeLanguage(lng, (err, t) => {
|
||||
if (!err) {
|
||||
this._updateLanguages();
|
||||
}
|
||||
callback && callback(err, t);
|
||||
});
|
||||
}
|
||||
changeLanguageWithPromise(lng: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.config!.lng = lng;
|
||||
this.instance.changeLanguage(lng, (err, t) => {
|
||||
if (err) {
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
reject({
|
||||
err,
|
||||
t,
|
||||
});
|
||||
}
|
||||
this._updateLanguages();
|
||||
resolve({ err, t });
|
||||
});
|
||||
});
|
||||
}
|
||||
getDir(lng: string) {
|
||||
return this.instance.dir(lng);
|
||||
}
|
||||
t<
|
||||
TResult extends TFunctionResult = string,
|
||||
TKeys extends TFunctionKeys = string,
|
||||
TInterpolationMap extends object = StringMap,
|
||||
>(
|
||||
keys: TKeys | TKeys[],
|
||||
options?: TOptions<TInterpolationMap> | string,
|
||||
fallbackText?: string,
|
||||
): TResult {
|
||||
const separatorMock = Array.isArray(keys)
|
||||
? Array.from(keys)
|
||||
.map(() => ' ')
|
||||
.join('')
|
||||
: Array(keys.length).fill(' ');
|
||||
|
||||
// fixed: 去除默认lngs,有lngs i18next就会忽略lng
|
||||
const opt: Record<string, any> = Object.assign(
|
||||
{ keySeparator: separatorMock, nsSeparator: separatorMock },
|
||||
options,
|
||||
);
|
||||
|
||||
return this.instance.t(
|
||||
keys as string,
|
||||
fallbackText as string,
|
||||
opt,
|
||||
) as TResult;
|
||||
}
|
||||
}
|
||||
27
frontend/packages/arch/i18n/src/intl/index.ts
Normal file
27
frontend/packages/arch/i18n/src/intl/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { type IIntlInitOptions, IntlModuleType, IntlModule } from './types';
|
||||
import Intl, { IntlInstance } from './i18n-impl';
|
||||
|
||||
export { default as I18nCore } from './i18n';
|
||||
|
||||
const i18n = IntlInstance;
|
||||
i18n.t = i18n.t.bind(i18n);
|
||||
const i18nConstructor = Intl;
|
||||
|
||||
export default i18n;
|
||||
export { i18n as I18n, Intl, i18nConstructor as I18nConstructor };
|
||||
62
frontend/packages/arch/i18n/src/intl/types.ts
Normal file
62
frontend/packages/arch/i18n/src/intl/types.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @stylistic/ts/comma-dangle */
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
/* eslint-disable prettier/prettier */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-invalid-void-type */
|
||||
import { type InitOptions } from 'i18next';
|
||||
|
||||
/**
|
||||
* 初始化 Intl 实例配置参数
|
||||
*/
|
||||
export interface IIntlInitOptions
|
||||
extends Omit<InitOptions, 'missingInterpolationHandler'> {
|
||||
/**
|
||||
* t 方法是否开启第三个参数兜底
|
||||
* @default true
|
||||
*/
|
||||
thirdParamFallback?: boolean;
|
||||
|
||||
/**
|
||||
* 忽略所有控制台输出,不建议设置为 true
|
||||
* @default false
|
||||
*/
|
||||
ignoreWarning?: boolean;
|
||||
}
|
||||
|
||||
export enum IntlModuleType {
|
||||
intl3rdParty = 'intl3rdParty',
|
||||
backend = 'backend',
|
||||
logger = 'logger',
|
||||
languageDetector = 'languageDetector',
|
||||
postProcessor = 'postProcessor',
|
||||
i18nFormat = 'i18nFormat',
|
||||
'3rdParty' = '3rdParty'
|
||||
}
|
||||
|
||||
export interface IntlModule<T extends keyof typeof IntlModuleType = keyof typeof IntlModuleType> {
|
||||
type: T
|
||||
name?: string
|
||||
init?: (i18n: any) => void | Promise<any>
|
||||
}
|
||||
|
||||
export type TFunctionKeys = string | TemplateStringsArray;
|
||||
|
||||
export type TFunctionResult = string | object | Array<string | object> | undefined | null;
|
||||
|
||||
export interface StringMap { [key: string]: any }
|
||||
67
frontend/packages/arch/i18n/src/raw/index.ts
Normal file
67
frontend/packages/arch/i18n/src/raw/index.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
|
||||
import locale from '../resource';
|
||||
export {
|
||||
type I18nKeysNoOptionsType,
|
||||
type I18nKeysHasOptionsType,
|
||||
} from '@coze-studio/studio-i18n-resource-adapter';
|
||||
import { I18n } from '../intl';
|
||||
|
||||
interface I18nConfig extends Record<string, unknown> {
|
||||
lng: 'en' | 'zh-CN';
|
||||
ns?: string;
|
||||
}
|
||||
export function initI18nInstance(config?: I18nConfig) {
|
||||
const { lng = 'en', ns, ...restConfig } = config || {};
|
||||
return new Promise(resolve => {
|
||||
I18n.use(LanguageDetector);
|
||||
I18n.init(
|
||||
{
|
||||
detection: {
|
||||
order: [
|
||||
'querystring',
|
||||
'cookie',
|
||||
'localStorage',
|
||||
'navigator',
|
||||
'htmlTag',
|
||||
],
|
||||
lookupQuerystring: 'lng',
|
||||
lookupCookie: 'i18next',
|
||||
lookupLocalStorage: 'i18next',
|
||||
fallback: 'zh-CN',
|
||||
caches: ['cookie'],
|
||||
mute: false,
|
||||
},
|
||||
react: {
|
||||
useSuspense: false,
|
||||
},
|
||||
keySeparator: false,
|
||||
fallbackLng: lng,
|
||||
lng,
|
||||
ns: ns || 'i18n',
|
||||
defaultNS: ns || 'i18n',
|
||||
resources: locale,
|
||||
...(restConfig ?? {}),
|
||||
},
|
||||
resolve,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export { I18n };
|
||||
19
frontend/packages/arch/i18n/src/resource.ts
Normal file
19
frontend/packages/arch/i18n/src/resource.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defaultConfig } from '@coze-studio/studio-i18n-resource-adapter';
|
||||
|
||||
export default defaultConfig;
|
||||
Reference in New Issue
Block a user