mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:39:05 +08:00
支持 mobile 和 en-US 替换配置;这里有个小 breaking change, 内置翻译文件 locale/en 换成了 locale/en-US (#1349)
* 环境配置覆盖初步 * 换成最靠前替换来支持 validations * 文档描述更清晰些
This commit is contained in:
parent
1933250052
commit
c092ccecac
@ -2,9 +2,16 @@
|
||||
title: 多语言
|
||||
---
|
||||
|
||||
amis 内置对英文的支持,同时你也可以扩展其他语言。
|
||||
amis 中对多语言的支持有两方面:
|
||||
|
||||
## JS SDK
|
||||
1. amis 内部组件的多语言,比如日期组件中的日期
|
||||
1. JSON 配置中的多语言,比如配置中的 label 值
|
||||
|
||||
## amis 内部组件多语言
|
||||
|
||||
分为 JS SDK 和 React 两种用法。
|
||||
|
||||
### JS SDK
|
||||
|
||||
从 1.1.0 版本开始已经自带英文翻译,所以只需要在 props 里设置 locale 即可。
|
||||
|
||||
@ -28,20 +35,20 @@ let amisScoped = amis.embed(
|
||||
}
|
||||
},
|
||||
{
|
||||
locale: 'en'
|
||||
locale: 'en-US'
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## React
|
||||
### React
|
||||
|
||||
React 中没有内置英文版本,需要自己 import,使用如下方法:
|
||||
React 版本中没有内置英文翻译,需要自己 import,使用如下方法:
|
||||
|
||||
```javascript
|
||||
import 'amis/lib/locale/en';
|
||||
import 'amis/lib/locale/en-US';
|
||||
```
|
||||
|
||||
在渲染 amis 组件的时候设置 locale 为 en
|
||||
在渲染 amis 组件的时候设置 locale 为 en-US
|
||||
|
||||
```javascript
|
||||
{
|
||||
@ -52,13 +59,33 @@ import 'amis/lib/locale/en';
|
||||
body: '内容'
|
||||
},
|
||||
{
|
||||
locale: 'en'
|
||||
locale: 'en-US'
|
||||
}
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 扩展其它语言
|
||||
## JSON 配置中设置多语言
|
||||
|
||||
在 JSON 配置中,也可以设置不同语言下的不同展现,比如前面设置了 `locale` 为 `en-US`,这时在任意 JSON 配置中都能使用 `en-US` 对象来覆盖这个语言下的效果。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"controls": [{
|
||||
"type": "text",
|
||||
"name": "name",
|
||||
"label": "姓名:",
|
||||
"en-US": {
|
||||
"label": "username: "
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
请点击上方的切换语言下拉框切换到英文,就能看到 `label` 属性被替换了,除了 `label` 以外,还可以覆盖其他任意属性,比如将 type 换成其他。
|
||||
|
||||
## 扩展内置组件的语言
|
||||
|
||||
如果想扩展其他语言,首先参考 `https://github.com/baidu/amis/blob/master/src/locale/en.ts` 文件,了解需要翻译哪些文字,以中文为 key,然后参考后面的示例注册新语言,未翻译的文字都将使用默认语言,即中文。
|
||||
|
||||
|
51
docs/zh-CN/extend/mobile.md
Normal file
51
docs/zh-CN/extend/mobile.md
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
title: 移动端定制
|
||||
---
|
||||
|
||||
有时候我们需要在移动端下展示不同效果,可以通过 `mobile` 属性来在移动端下覆盖部分属性。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"controls": [{
|
||||
"name": "email",
|
||||
"type": "email",
|
||||
"label": "邮箱:",
|
||||
"mobile": {
|
||||
"name": "phone",
|
||||
"type": "text",
|
||||
"label": "电话:",
|
||||
"validations": {
|
||||
"isPhoneNumber": true
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
请点击上方切换到移动端预览效果。
|
||||
|
||||
`mobile` 属性可以出现在配置中的任意地方,替换父节点的任意属性,比如前面的例子可以写成放在 `form` 上替换所有 `controls`
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"controls": [{
|
||||
"name": "email",
|
||||
"type": "email",
|
||||
"label": "邮箱:"
|
||||
}],
|
||||
"mobile": {
|
||||
"controls": [{
|
||||
"name": "phone",
|
||||
"type": "text",
|
||||
"label": "电话:",
|
||||
"validations": {
|
||||
"isPhoneNumber": true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 注意这里对于移动端的判断是根据页面宽度,和 CSS 保持一致,所以即便是在 PC 上,如果页面宽度很小也会切换到 mobile 配置
|
@ -113,7 +113,7 @@ let amisScoped = amis.embed(
|
||||
amisJSON,
|
||||
{
|
||||
// 这里是初始 props,一般不用传。
|
||||
// locale: 'en' // props 中可以设置语言,默认是中文
|
||||
// locale: 'en-US' // props 中可以设置语言,默认是中文
|
||||
},
|
||||
{
|
||||
// 可以不传,用来实现 ajax 请求
|
||||
@ -272,7 +272,7 @@ class MyComponent extends React.Component<any, any> {
|
||||
},
|
||||
{
|
||||
// props...
|
||||
// locale: 'en' // 请参考「多语言」的文档
|
||||
// locale: 'en-US' // 请参考「多语言」的文档
|
||||
},
|
||||
{
|
||||
// 下面三个接口必须实现
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
} from '../../src/components/index';
|
||||
import {eachTree, mapTree} from '../../src/utils/helper';
|
||||
import {Icon} from '../../src/components/icons';
|
||||
import '../../src/locale/en';
|
||||
import '../../src/locale/en-US';
|
||||
import {
|
||||
Router,
|
||||
Route,
|
||||
@ -72,7 +72,7 @@ const locales = [
|
||||
|
||||
{
|
||||
label: 'English',
|
||||
value: 'en'
|
||||
value: 'en-US'
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -1169,6 +1169,13 @@ export default [
|
||||
// @ts-ignore
|
||||
import('../../docs/zh-CN/extend/addon.md').then(makeMarkdownRenderer)
|
||||
},
|
||||
{
|
||||
label: '移动端定制',
|
||||
path: '/zh-CN/docs/extend/mobile',
|
||||
getComponent: () =>
|
||||
// @ts-ignore
|
||||
import('../../docs/zh-CN/extend/mobile.md').then(makeMarkdownRenderer)
|
||||
},
|
||||
{
|
||||
label: '多语言',
|
||||
path: '/zh-CN/docs/extend/i18n',
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
render as renderAmis
|
||||
} from '../src/index';
|
||||
|
||||
import '../src/locale/en';
|
||||
import '../src/locale/en-US';
|
||||
|
||||
export function embed(
|
||||
container: string | HTMLElement,
|
||||
|
@ -9,7 +9,7 @@ import {render} from 'react-dom';
|
||||
import axios from 'axios';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import {toast} from '../src/components/Toast';
|
||||
import '../src/locale/en';
|
||||
import '../src/locale/en-US';
|
||||
|
||||
import {render as renderAmis} from '../src/index';
|
||||
|
||||
|
30
src/envOverwrite.ts
Normal file
30
src/envOverwrite.ts
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @file 用于在移动端或不同语言环境下使用不同配置
|
||||
*/
|
||||
|
||||
import {findObjectsWithKey} from './utils/helper';
|
||||
|
||||
const isMobile = window.matchMedia('(max-width: 768px)').matches ? true : false;
|
||||
|
||||
export const envOverwrite = (schema: any, locale?: string) => {
|
||||
if (schema.mobile && isMobile) {
|
||||
Object.assign(schema, schema.mobile);
|
||||
delete schema.mobile;
|
||||
}
|
||||
|
||||
if (locale) {
|
||||
let schemaNodes = findObjectsWithKey(schema, locale);
|
||||
for (let schemaNode of schemaNodes) {
|
||||
Object.assign(schemaNode, schemaNode[locale]);
|
||||
delete schemaNode[locale];
|
||||
}
|
||||
}
|
||||
|
||||
if (isMobile) {
|
||||
let schemaNodes = findObjectsWithKey(schema, 'mobile');
|
||||
for (let schemaNode of schemaNodes) {
|
||||
Object.assign(schemaNode, schemaNode['mobile']);
|
||||
delete schemaNode['mobile'];
|
||||
}
|
||||
}
|
||||
};
|
@ -56,6 +56,7 @@ import {
|
||||
} from './locale';
|
||||
import {SchemaCollection, SchemaObject, SchemaTpl} from './Schema';
|
||||
import {result} from 'lodash';
|
||||
import {envOverwrite} from './envOverwrite';
|
||||
|
||||
export interface TestFunc {
|
||||
(
|
||||
@ -156,7 +157,7 @@ export interface RendererConfig extends RendererBasicConfig {
|
||||
}
|
||||
|
||||
export interface RenderSchemaFilter {
|
||||
(schema: Schema, renderer: RendererConfig, props?: object): Schema;
|
||||
(schema: Schema, renderer: RendererConfig, props?: any): Schema;
|
||||
}
|
||||
|
||||
export interface RootRenderProps {
|
||||
@ -425,6 +426,9 @@ export class RootRenderer extends React.Component<RootRendererProps> {
|
||||
)
|
||||
: data;
|
||||
|
||||
// 根据环境覆盖 schema,这个要在最前面做,不然就无法覆盖 validations
|
||||
envOverwrite(schema, locale);
|
||||
|
||||
return (
|
||||
<RootStoreContext.Provider value={rootStore}>
|
||||
<ThemeContext.Provider value={this.props.theme || 'default'}>
|
||||
@ -1058,7 +1062,12 @@ export function render(
|
||||
options: RenderOptions = {},
|
||||
pathPrefix: string = ''
|
||||
): JSX.Element {
|
||||
const locale = props.locale || getDefaultLocale();
|
||||
let locale = props.locale || getDefaultLocale();
|
||||
// 兼容 locale 的不同写法
|
||||
locale = locale.replace('_', '-');
|
||||
locale = locale === 'en' ? 'en-US' : locale;
|
||||
locale = locale === 'zh' ? 'zh-CN' : locale;
|
||||
locale = locale === 'cn' ? 'zh-CN' : locale;
|
||||
const translate = props.translate || makeTranslator(locale);
|
||||
let store = stores[options.session || 'global'];
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {register} from '../locale';
|
||||
|
||||
register('en', {
|
||||
register('en-US', {
|
||||
'确认': 'Confirm',
|
||||
'取消': 'Cancel',
|
||||
'YYYY年': 'YYYY',
|
@ -14,7 +14,7 @@ import {
|
||||
} from '../utils/tpl-builtin';
|
||||
import {isApiOutdated, isEffectiveApi} from '../utils/api';
|
||||
import {ScopedContext, IScopedContext} from '../Scoped';
|
||||
import {createObject} from '../utils/helper';
|
||||
import {createObject, findObjectsWithKey} from '../utils/helper';
|
||||
import Spinner from '../components/Spinner';
|
||||
import {
|
||||
BaseSchema,
|
||||
@ -117,24 +117,6 @@ export interface ChartSchema extends BaseSchema {
|
||||
unMountOnHidden?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 深度查找具有某个 key 名字段的对象
|
||||
* @param obj
|
||||
* @param key
|
||||
*/
|
||||
function findObjectsWithKey(obj: any, key: string) {
|
||||
let objects: any[] = [];
|
||||
for (const k in obj) {
|
||||
if (!obj.hasOwnProperty(k)) continue;
|
||||
if (typeof obj[k] === 'object') {
|
||||
objects = objects.concat(findObjectsWithKey(obj[k], key));
|
||||
} else if (k === key) {
|
||||
objects.push(obj);
|
||||
}
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
|
||||
const EVAL_CACHE: {[key: string]: Function} = {};
|
||||
/**
|
||||
* ECharts 中有些配置项可以写函数,但 JSON 中无法支持,为了实现这个功能,需要将看起来像函数的字符串转成函数类型
|
||||
|
@ -1342,3 +1342,21 @@ export const keyToPath = (string: string) => {
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* 深度查找具有某个 key 名字段的对象
|
||||
* @param obj
|
||||
* @param key
|
||||
*/
|
||||
export function findObjectsWithKey(obj: any, key: string) {
|
||||
let objects: any[] = [];
|
||||
for (const k in obj) {
|
||||
if (!obj.hasOwnProperty(k)) continue;
|
||||
if (k === key) {
|
||||
objects.push(obj);
|
||||
} else if (typeof obj[k] === 'object') {
|
||||
objects = objects.concat(findObjectsWithKey(obj[k], key));
|
||||
}
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user