import { IApi } from 'umi';
import { join, dirname } from 'path';
import { readFileSync, existsSync } from 'fs';
import {
IGetLocaleFileListResult,
IAddAntdLocales,
getLocaleList,
isNeedPolyfill,
exactLocalePaths,
getMomentLocale,
getAntdLocale,
} from './utils';
interface ILocaleConfig {
default?: string;
baseNavigator?: boolean;
useLocalStorage?: boolean;
/** title 开启国际化 */
title?: boolean;
antd?: boolean;
baseSeparator?: string;
}
let hasAntd = false;
try {
hasAntd = !!require.resolve('antd');
} catch (_) {
console.log(
'@umijs/plugin-locale WARNING: antd is not installed, unavailable.',
);
}
export const packageNormalize = (packageName: string) =>
packageName.replace(/[\@\/\-\.]/g, '_');
export default (api: IApi) => {
const {
paths,
utils: { Mustache, winPath, lodash },
} = api;
api.describe({
key: 'locale',
config: {
default: {
baseNavigator: true,
useLocalStorage: true,
baseSeparator: '-',
antd: !!hasAntd,
},
schema(joi) {
return joi.object({
default: joi.string(),
useLocalStorage: joi.boolean(),
baseNavigator: joi.boolean(),
title: joi.boolean(),
antd: joi.boolean(),
baseSeparator: joi.string(),
});
},
},
enableBy: api.EnableBy.config,
});
const reactIntlPkgPath = winPath(
dirname(require.resolve('react-intl/package')),
);
api.addDepInfo(() => {
return {
name: 'react-intl',
range: require('../package.json').dependencies['react-intl'],
alias: [reactIntlPkgPath],
};
});
// polyfill
if (isNeedPolyfill(api.userConfig?.targets || {})) {
api.addEntryImportsAhead(() => ({
source: require.resolve('intl'),
}));
}
const addAntdLocales: IAddAntdLocales = async (args) =>
await api.applyPlugins({
key: 'addAntdLocales',
type: api.ApplyPluginsType.add,
initialValue: [
`antd/${api.config?.ssr ? 'lib' : 'es'}/locale/${getAntdLocale(
args.lang,
args.country,
)}`,
],
args,
});
const getList = async (): Promise => {
return getLocaleList({
localeFolder: api.config?.singular ? 'locale' : 'locales',
separator: api.config.locale?.baseSeparator,
absSrcPath: paths.absSrcPath,
absPagesPath: paths.absPagesPath,
addAntdLocales,
});
};
// add runtime locale
api.addRuntimePluginKey(() => 'locale');
// 生成临时文件
api.onGenerateFiles(async () => {
const localeTpl = readFileSync(
join(winPath(__dirname), 'templates', 'locale.tpl'),
'utf-8',
);
const { baseSeparator, baseNavigator, antd, title, useLocalStorage } = api
.config.locale as ILocaleConfig;
const defaultLocale = api.config.locale?.default || `zh${baseSeparator}CN`;
const localeList = await getList();
const momentLocales = localeList
.map(({ momentLocale }) => momentLocale)
.filter((locale) => locale);
const antdLocales = localeList
.map(({ antdLocale }) => antdLocale)
.filter((locale) => locale);
let MomentLocales = momentLocales;
let DefaultMomentLocale = '';
// set moment default accounding to locale.default
if (!MomentLocales.length && api.config.locale?.default) {
const [lang, country = ''] = defaultLocale.split(baseSeparator);
const { momentLocale } = getMomentLocale(lang, country);
if (momentLocale) {
MomentLocales = [momentLocale];
DefaultMomentLocale = momentLocale;
}
}
let DefaultAntdLocales: string[] = [];
// set antd default locale
if (!antdLocales.length && api.config.locale?.antd) {
const [lang, country = ''] = defaultLocale.split(baseSeparator);
DefaultAntdLocales = lodash.uniq(
await addAntdLocales({
lang,
country,
}),
);
}
const NormalizeAntdLocalesName = function () {
// @ts-ignore
return packageNormalize(this);
};
api.writeTmpFile({
content: Mustache.render(localeTpl, {
MomentLocales,
DefaultMomentLocale,
NormalizeAntdLocalesName,
DefaultAntdLocales,
Antd: !!antd,
Title: title && api.config.title,
BaseSeparator: baseSeparator,
DefaultLocale: defaultLocale,
DefaultLang: defaultLocale,
}),
path: 'plugin-locale/locale.tsx',
});
const localeExportsTpl = readFileSync(
join(__dirname, 'templates', 'localeExports.tpl'),
'utf-8',
);
const localeDirName = api.config.singular ? 'locale' : 'locales';
const localeDirPath = join(api.paths!.absSrcPath!, localeDirName);
api.writeTmpFile({
path: 'plugin-locale/localeExports.ts',
content: Mustache.render(localeExportsTpl, {
BaseSeparator: baseSeparator,
BaseNavigator: baseNavigator,
UseLocalStorage: !!useLocalStorage,
LocaleDir: localeDirName,
ExistLocaleDir: existsSync(localeDirPath),
LocaleList: localeList.map((locale) => ({
...locale,
antdLocale: locale.antdLocale.map((antdLocale, index) => ({
locale: antdLocale,
index: index,
})),
paths: locale.paths.map((path, index) => ({
path,
index,
})),
})),
Antd: !!antd,
DefaultLocale: JSON.stringify(defaultLocale),
warningPkgPath: winPath(require.resolve('warning')),
reactIntlPkgPath,
}),
});
// runtime.tsx
const runtimeTpl = readFileSync(
join(__dirname, 'templates', 'runtime.tpl'),
'utf-8',
);
api.writeTmpFile({
path: 'plugin-locale/runtime.tsx',
content: Mustache.render(runtimeTpl, {
Title: !!title,
}),
});
// SelectLang.tsx
const selectLang = readFileSync(
join(__dirname, 'templates', 'SelectLang.tpl'),
'utf-8',
);
api.writeTmpFile({
path: 'plugin-locale/SelectLang.tsx',
content: Mustache.render(selectLang, {
Antd: !!antd,
LocaleList: localeList,
ShowSelectLang: localeList.length > 1 && !!antd,
antdFiles: api.config?.ssr ? 'lib' : 'es',
}),
});
});
// Runtime Plugin
api.addRuntimePlugin(() => '../plugin-locale/runtime.tsx');
// Modify entry js
api.addEntryCodeAhead(() =>
`import { _onCreate } from './plugin-locale/locale';\n_onCreate();`.trim(),
);
// watch locale files
api.addTmpGenerateWatcherPaths(async () => {
const localeList = await getList();
return exactLocalePaths(localeList);
});
api.addUmiExports(() => {
return {
exportAll: true,
source: `../plugin-locale/localeExports`,
};
});
api.addUmiExports(() => {
return {
exportAll: true,
source: `../plugin-locale/SelectLang`,
};
});
};