You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tailchat/client/shared/i18n/index.ts

151 lines
3.5 KiB
TypeScript

import i18next, {
StringMap,
TFunctionKeys,
TOptions,
TOptionsBase,
} from 'i18next';
import {
useTranslation as useI18NTranslation,
initReactI18next,
} from 'react-i18next';
import { crc32 } from 'crc';
import { languageDetector } from './language';
import { useState, useEffect } from 'react';
import HttpApi from 'i18next-http-backend'; // https://github.com/i18next/i18next-http-backend
/**
* 允许出现的语言
*/
export type AllowedLanguage = 'zh-CN' | 'en-US';
i18next
.use(languageDetector)
.use(HttpApi)
.use(initReactI18next)
.init({
fallbackLng: 'zh-CN',
load: 'currentOnly',
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json',
allowMultiLoading: false,
addPath: (...args: any[]) => {
console.log('缺少翻译:', ...args);
},
},
react: {
// Reference: https://react.i18next.com/latest/trans-component#i-18-next-options
hashTransKey(defaultValue: string) {
// return a key based on defaultValue or if you prefer to just remind you should set a key return false and throw an error
return `k${crc32(defaultValue).toString(16)}`;
},
},
} as any);
type TFunctionResult = string | React.ReactNode;
// Fork from i18next
interface TFunction {
// basic usage
<TKeys extends TFunctionKeys = string>(key: TKeys): string;
<
TResult extends TFunctionResult = string,
TKeys extends TFunctionKeys = string,
TInterpolationMap extends object = StringMap
>(
key: TKeys,
options?: TOptions<TInterpolationMap> | string
): TResult;
// overloaded usage
<
TResult extends TFunctionResult = string,
TKeys extends TFunctionKeys = string,
TInterpolationMap extends object = StringMap
>(
key: TKeys,
defaultValue?: string,
options?: TOptions<TInterpolationMap> | string
): TResult;
}
/**
* 国际化翻译
*/
export const t: TFunction = (
key: string,
defaultValue?: string,
options?: TOptionsBase & Record<string, unknown>
) => {
try {
const hashKey = `k${crc32(key).toString(16)}`;
let words = i18next.t(hashKey, defaultValue, options);
if (words === hashKey) {
words = key;
console.info(`[i18n] 翻译缺失: [${hashKey}]${key}`);
}
return words;
} catch (err) {
console.error(err);
return key;
}
};
/**
* 本地翻译
* @example
* localTrans({'zh-CN': '你好', 'en-US': 'Hello'});
*
* @param trans 翻译对象
*/
export function localTrans(trans: Record<AllowedLanguage, string>) {
const lang = i18next.language as AllowedLanguage;
return trans[lang] ?? trans['zh-CN'] ?? trans['en-US'];
}
/**
* 设置i18next的语言
*/
export async function setLanguage(lang: AllowedLanguage): Promise<void> {
return new Promise((resolve, reject) => {
i18next.changeLanguage(lang, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
/**
* 获取i18n语言
*/
export function getLanguage(): string {
return i18next.language;
}
/**
* 监听语言变更
*/
export function onLanguageChange(cb: (lang: string) => void) {
i18next.on('languageChanged', cb);
}
/**
* fork from i18next/react-i18next/-/blob/src/useTranslation.js
* i18n for react 使用hooks
*/
export function useTranslation() {
const { t: i18nT, ready } = useI18NTranslation();
const [_t, _setT] = useState<TFunction>(() => t);
useEffect(() => {
_setT(
() =>
(...args: any[]) =>
(t as any)(...args)
);
}, [i18nT]);
return { t: _t, ready };
}