feat: load multiple languages dynamically (#1355)

* feat: load multiple languages dynamically

* fix: map locale

* fix: antd

* fix: locale

* fix: th

* fix: cronstrue locales

* fix: improve code

* fix: defaults
This commit is contained in:
chenos 2023-01-13 10:55:04 +08:00 committed by GitHub
parent a1127300ae
commit cc47041519
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 2307 additions and 393 deletions

View File

@ -31,7 +31,9 @@ RUN cd /app \
&& rm -rf my-nocobase-app/packages/app/client/src/.umi \
&& rm -rf nocobase.tar.gz \
&& rm -rf ./my-nocobase-app/node_modules/@antv \
&& rm -rf ./my-nocobase-app/node_modules/antd \
&& rm -rf ./my-nocobase-app/node_modules/antd/dist \
&& rm -rf ./my-nocobase-app/node_modules/antd/es \
&& rm -rf ./my-nocobase-app/node_modules/antd/node_modules \
&& rm -rf ./my-nocobase-app/node_modules/@ant-design \
&& rm -rf ./my-nocobase-app/node_modules/china-division/dist/villages.json \
&& find ./my-nocobase-app/node_modules/china-division/dist -name '*.csv' -delete \

View File

@ -30,9 +30,11 @@ RUN cd /app \
RUN cd /app \
&& rm -rf my-nocobase-app/packages/app/client/src/.umi \
&& rm -rf nocobase.tar.gz \
&& rm -rf ./my-nocobase-app/nocobase/node_modules/@antv \
&& rm -rf ./my-nocobase-app/nocobase/node_modules/antd \
&& rm -rf ./my-nocobase-app/nocobase/node_modules/@ant-design \
&& rm -rf ./my-nocobase-app/node_modules/@antv \
&& rm -rf ./my-nocobase-app/node_modules/antd/dist \
&& rm -rf ./my-nocobase-app/node_modules/antd/es \
&& rm -rf ./my-nocobase-app/node_modules/antd/node_modules \
&& rm -rf ./my-nocobase-app/node_modules/@ant-design \
&& rm -rf ./my-nocobase-app/node_modules/china-division/dist/villages.json \
&& find ./my-nocobase-app/node_modules/china-division/dist -name '*.csv' -delete \
&& find ./my-nocobase-app/node_modules/china-division/dist -name '*.sqlite' -delete \

View File

@ -22,7 +22,8 @@
"cron-parser": "^4.6.0",
"cronstrue": "^2.11.0",
"file-saver": "^2.0.5",
"i18next": "^21.6.0",
"i18next": "^22.4.9",
"i18next-http-backend": "^2.1.1",
"json-templates": "^4.2.0",
"marked": "^4.0.12",
"mathjs": "^10.6.0",

View File

@ -1,14 +1,21 @@
import { ConfigProvider, Spin } from 'antd';
import React from 'react';
import moment from 'moment';
import React, { createContext, useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { useAPIClient, useRequest } from '../api-client';
import locale from '../locale';
import { loadConstrueLocale } from './loadConstrueLocale';
export const AppLangContext = createContext<any>({});
export const useAppLangContext = () => {
return useContext(AppLangContext);
};
export function AntdConfigProvider(props) {
const { remoteLocale, ...others } = props;
const api = useAPIClient();
const { i18n } = useTranslation();
const { loading } = useRequest(
const { data, loading } = useRequest(
{
url: 'app:getLang',
},
@ -19,6 +26,12 @@ export function AntdConfigProvider(props) {
api.auth.setLocale(data?.data?.lang);
i18n.changeLanguage(data?.data?.lang);
}
Object.keys(data?.data?.resources || {}).forEach((key) => {
i18n.addResources(data?.data?.lang, key, data?.data?.resources[key] || {});
});
loadConstrueLocale(data?.data);
moment.locale(data?.data?.moment);
window['cronLocale'] = data?.data?.cron;
},
manual: !remoteLocale,
},
@ -27,12 +40,10 @@ export function AntdConfigProvider(props) {
return <Spin />;
}
return (
<ConfigProvider
dropdownMatchSelectWidth={false}
{...others}
locale={locale[i18n.language].antd}
>
{props.children}
</ConfigProvider>
<AppLangContext.Provider value={data?.data}>
<ConfigProvider dropdownMatchSelectWidth={false} {...others} locale={data?.data?.antd || {}}>
{props.children}
</ConfigProvider>
</AppLangContext.Provider>
);
}

View File

@ -0,0 +1,172 @@
import cronstrue from 'cronstrue';
class CronstrueLocale {
constructor(protected data: any) {}
atX0SecondsPastTheMinuteGt20(): string | null {
return this.data['atX0SecondsPastTheMinuteGt20'];
}
atX0MinutesPastTheHourGt20(): string | null {
return this.data['atX0MinutesPastTheHourGt20'];
}
commaMonthX0ThroughMonthX1(): string | null {
return this.data['commaMonthX0ThroughMonthX1'];
}
commaYearX0ThroughYearX1(): string | null {
return this.data['commaYearX0ThroughYearX1'];
}
use24HourTimeFormatByDefault() {
return this.data['use24HourTimeFormatByDefault'];
}
anErrorOccuredWhenGeneratingTheExpressionD() {
return this.data['anErrorOccuredWhenGeneratingTheExpressionD'];
}
everyMinute() {
return this.data['everyMinute'];
}
everyHour() {
return this.data['everyHour'];
}
atSpace() {
return this.data['atSpace'];
}
everyMinuteBetweenX0AndX1() {
return this.data['everyMinuteBetweenX0AndX1'];
}
at() {
return this.data['at'];
}
spaceAnd() {
return this.data['spaceAnd'];
}
everySecond() {
return this.data['everySecond'];
}
everyX0Seconds() {
return this.data['everyX0Seconds'];
}
secondsX0ThroughX1PastTheMinute() {
return this.data['secondsX0ThroughX1PastTheMinute'];
}
atX0SecondsPastTheMinute() {
return this.data['atX0SecondsPastTheMinute'];
}
everyX0Minutes() {
return this.data['everyX0Minutes'];
}
minutesX0ThroughX1PastTheHour() {
return this.data['minutesX0ThroughX1PastTheHour'];
}
atX0MinutesPastTheHour() {
return this.data['atX0MinutesPastTheHour'];
}
everyX0Hours() {
return this.data['everyX0Hours'];
}
betweenX0AndX1() {
return this.data['betweenX0AndX1'];
}
atX0() {
return this.data['atX0'];
}
commaEveryDay() {
return this.data['commaEveryDay'];
}
commaEveryX0DaysOfTheWeek() {
return this.data['commaEveryX0DaysOfTheWeek'];
}
commaX0ThroughX1() {
return this.data['commaX0ThroughX1'];
}
commaAndX0ThroughX1() {
return this.data['commaAndX0ThroughX1'];
}
first() {
return this.data['first'];
}
second() {
return this.data['second'];
}
third() {
return this.data['third'];
}
fourth() {
return this.data['fourth'];
}
fifth() {
return this.data['fifth'];
}
commaOnThe() {
return this.data['commaOnThe'];
}
spaceX0OfTheMonth() {
return this.data['spaceX0OfTheMonth'];
}
lastDay() {
return this.data['lastDay'];
}
commaOnTheLastX0OfTheMonth() {
return this.data['commaOnTheLastX0OfTheMonth'];
}
commaOnlyOnX0() {
return this.data['commaOnlyOnX0'];
}
commaAndOnX0() {
return this.data['commaAndOnX0'];
}
commaEveryX0Months() {
return this.data['commaEveryX0Months'];
}
commaOnlyInX0() {
return this.data['commaOnlyInX0'];
}
commaOnTheLastDayOfTheMonth() {
return this.data['commaOnTheLastDayOfTheMonth'];
}
commaOnTheLastWeekdayOfTheMonth() {
return this.data['commaOnTheLastWeekdayOfTheMonth'];
}
commaDaysBeforeTheLastDayOfTheMonth() {
return this.data['commaDaysBeforeTheLastDayOfTheMonth'];
}
firstWeekday() {
return this.data['firstWeekday'];
}
weekdayNearestDayX0() {
return this.data['weekdayNearestDayX0'];
}
commaOnTheX0OfTheMonth() {
return this.data['commaOnTheX0OfTheMonth'];
}
commaEveryX0Days() {
return this.data['commaEveryX0Days'];
}
commaBetweenDayX0AndX1OfTheMonth() {
return this.data['commaBetweenDayX0AndX1OfTheMonth'];
}
commaOnDayX0OfTheMonth() {
return this.data['commaOnDayX0OfTheMonth'];
}
commaEveryHour() {
return this.data['commaEveryHour'];
}
commaEveryX0Years() {
return this.data['commaEveryX0Years'];
}
commaStartingX0() {
return this.data['commaStartingX0'];
}
daysOfTheWeek() {
return this.data['daysOfTheWeek'];
}
monthsOfTheYear() {
return this.data['monthsOfTheYear'];
}
}
export const loadConstrueLocale = (data) => {
cronstrue.initialize({
load(availableLocales) {
availableLocales[data?.lang] = new CronstrueLocale(data?.cronstrue);
},
});
};

View File

@ -1,5 +1,4 @@
import i18next from 'i18next';
import moment from 'moment';
import { initReactI18next } from 'react-i18next';
import locale from '../locale';
const log = require('debug')('i18next');
@ -12,27 +11,25 @@ Object.keys(locale).forEach((lang) => {
resources[lang] = locale[lang].resources;
});
i18n.use(initReactI18next).init({
lng: localStorage.getItem('NOCOBASE_LOCALE') || 'en-US',
// debug: true,
defaultNS: 'client',
// parseMissingKeyHandler: (key) => {
// console.log('parseMissingKeyHandler', `'${key}': '${key}',`);
// return key;
// },
// ns: ['client'],
resources,
});
function setMomentLng(language) {
const lng = locale[language || 'en-US'].moment || 'en';
log(lng);
moment.locale(lng);
}
setMomentLng(localStorage.getItem('NOCOBASE_LOCALE'));
i18n
// .use(Backend)
.use(initReactI18next)
.init({
lng: localStorage.getItem('NOCOBASE_LOCALE') || 'en-US',
// debug: true,
defaultNS: 'client',
// backend: {
// // for all available options read the backend's repository readme file
// loadPath: '/api/locales/{{lng}}/{{ns}}.json',
// },
// parseMissingKeyHandler: (key) => {
// console.log('parseMissingKeyHandler', `'${key}': '${key}',`);
// return key;
// },
// ns: ['client'],
resources: {},
});
i18n.on('languageChanged', (lng) => {
localStorage.setItem('NOCOBASE_LOCALE', lng);
setMomentLng(lng);
});

View File

@ -4,6 +4,7 @@ import './global.less';
export * from './acl';
export * from './antd-config-provider';
export * from './api-client';
export * from './appInfo';
export * from './application';
export * from './async-data-provider';
export * from './block-provider';
@ -26,5 +27,4 @@ export * from './schema-templates';
export * from './settings-form';
export * from './system-settings';
export * from './user';
export * from './appInfo'

View File

@ -43,6 +43,7 @@ export default {
"Super admin": "Super admin",
"Language": "Language",
"Allow sign up": "Allow sign up",
"Enable SMS authentication": "Enable SMS authentication",
"Sign out": "Sign out",
"Cancel": "Cancel",
"Submit": "Submit",
@ -53,12 +54,12 @@ export default {
"Form": "Form",
"Select data source": "Select data source",
"Calendar": "Calendar",
'Delete events': 'Delete events',
'This event': 'This event',
'This and following events': 'This and following events',
'All events': 'All events',
'Delete this event?': 'Delete this event?',
'Delete Event': 'Delete Event',
"Delete events": "Delete events",
"This event": "This event",
"This and following events": "This and following events",
"All events": "All events",
"Delete this event?": "Delete this event?",
"Delete Event": "Delete Event",
"Kanban": "Kanban",
"Select grouping field": "Select grouping field",
"Media": "Media",
@ -114,11 +115,12 @@ export default {
"Collection display name": "Collection display name",
"Collection name": "Collection name",
"Inherits": "Inherits",
"AutoGenId": "Auto-generated ID field",
"CreatedBy": "Recording a row's created user",
"UpdatedBy": "Recording a row's last updated user",
"CreatedAt": "Recording a row's created time ",
"UpdatedAt": "Recording a row's last updated user",
"Generate ID field automatically": "Generate ID field automatically",
"Store the creation user of each record": "Store the creation user of each record",
"Store the last update user of each record": "Store the last update user of each record",
"Store the creation time of each record": "Store the creation time of each record",
"Store the last update time of each record": "Store the last update time of each record",
"More options": "More options",
"Records can be sorted": "Records can be sorted",
"Collection template": "Collection template",
"Calendar collection": "Calendar collection",
@ -136,9 +138,9 @@ export default {
"Association fields filter": "Association fields filter",
"PK & FK fields": "PK & FK fields",
"Association fields": "Association fields",
"Parent collection fields": "Parent collection fields",
"System fields": "System fields",
"General fields": "General fields",
"Parent collection fields": "Parent collection fields",
"Basic": "Basic",
"Single line text": "Single line text",
"Long text": "Long text",
@ -183,6 +185,7 @@ export default {
"12 hour": "12 hour",
"24 hour": "24 hour",
"Relationship type": "Relationship type",
"Inverse relationship type": "Inverse relationship type",
"Source collection": "Source collection",
"Source key": "Source key",
"Target collection": "Target collection",
@ -193,12 +196,28 @@ export default {
"One to many": "One to many",
"Many to one": "Many to one",
"Many to many": "Many to many",
"Foreign key 1": "Foreign key 1",
"Foreign key 2": "Foreign key 2",
"One to one description": "Used to create one-to-one relationships. For example, a user has a profile.",
"One to many description": "Used to create a one-to-many relationship. For example, a country will have many cities and a city can only be in one country. When present as a field, it is a sub-table that displays the records of the associated collection. When created, a Many-to-one field is automatically generated in the associated collection.",
"Many to one description": "Used to create many-to-one relationships. For example, a city can belong to only one country and a country can have many cities. When present as a field, it is a drop-down selection used to select record from the associated collection. Once created, a One-to-many field is automatically generated in the associated collection.",
"Many to many description": "Used to create many-to-many relationships. For example, a student will have many teachers and a teacher will have many students. When present as a field, it is a drop-down selection used to select records from the associated collection.",
"Foreign key 1": "Foreign key 1",
"Foreign key 2": "Foreign key 2",
"Generated automatically if left blank": "Generated automatically if left blank",
"Display association fields": "Display association fields",
"Field component": "Field component",
"Subtable": "Subtable",
"Subform": "Subform",
"Record picker": "Record picker",
"Toggles the subfield mode": "Toggles the subfield mode",
"Selector mode": "Selector mode",
"Subtable mode": "Subtable mode",
"Subform mode": "Subform mode",
"Edit block title": "Edit block title",
"Block title": "Block title",
"Pattern": "Pattern",
"Editable": "Editable",
"Readonly": "Readonly",
"Easy-reading": "Easy-reading",
"Add filter": "Add filter",
"Add filter group": "Add filter group",
"Comparision": "Comparision",
@ -219,6 +238,7 @@ export default {
"Edit button": "Edit button",
"Hide": "Hide",
"Enable actions": "Enable actions",
"Import": "Import",
"Export": "Export",
"Customize": "Customize",
"Function": "Function",
@ -239,9 +259,30 @@ export default {
"Custom column name": "Custom column name",
"Edit description": "Edit description",
"Required": "Required",
"Unique": "Unique",
"Label field": "Label field",
"Default is the ID field": "Default is the ID field",
"Set default sorting rules": "Set default sorting rules",
"Set validation rules": "Set validation rules",
"Max length": "Max length",
"Min length": "Min length",
"Maximum": "Maximum",
"Minimum": "Minimum",
"Max length must greater than min length": "Max length must greater than min length",
"Min length must less than max length": "Min length must less than max length",
"Maximum must greater than minimum": "Maximum must greater than minimum",
"Minimum must less than maximum": "Minimum must less than maximum",
"Validation rule": "Validation rule",
"Add validation rule": "Add validation rule",
"Format": "Format",
"Regular expression": "Pattern",
"Error message": "Error message",
"Length": "Length",
"The field value cannot be greater than ": "The field value cannot be greater than ",
"The field value cannot be less than ": "The field value cannot be less than ",
"The field value is not an integer number": "The field value is not an integer number",
"Set default value": "Set default value",
"Default value": "Default value",
"is before": "is before",
"is after": "is after",
"is on or after": "is on or after",
@ -275,6 +316,7 @@ export default {
"Configure calendar": "Configure calendar",
"Title field": "Title field",
"Custom title": "Custom title",
"Show lunar": "Show lunar",
"Start date field": "Start date field",
"End date field": "End date field",
"Navigate": "Navigate",
@ -282,10 +324,13 @@ export default {
"Description": "Description",
"Select view": "Select view",
"Reset": "Reset",
"Importable fields": "Importable fields",
"Exportable fields": "Exportable fields",
"Saved successfully": "Saved successfully",
"Nickname": "Nickname",
"Sign in": "Sign in",
"Sign in via account": "Sign in via account",
"Sign in via phone": "Sign in via phone",
"Create an account": "Create an account",
"Sign up": "Sign up",
"Confirm password": "Confirm password",
@ -293,6 +338,9 @@ export default {
"Signed up successfully. It will jump to the login page.": "Signed up successfully. It will jump to the login page.",
"Password mismatch": "Password mismatch",
"Users": "Users",
"Verification code": "Verification code",
"Send code": "Send code",
"Retry after {{count}} seconds": "Retry after {{count}} seconds",
"Roles": "Roles",
"Add role": "Add role",
"Role name": "Role name",
@ -355,6 +403,7 @@ export default {
"DESC": "DESC",
"Add sort field": "Add sort field",
"ID": "ID",
"Identifier for program usage. Support letters, numbers and underscores, must start with an letter.": "Identifier for program usage. Support letters, numbers and underscores, must start with an letter.",
"Drawer": "Drawer",
"Dialog": "Dialog",
"Delete action": "Delete action",
@ -377,6 +426,7 @@ export default {
"exists": "exists",
"not exists": "not exists",
"is current logged-in user": "is current logged-in user",
"is not current logged-in user": "is not current logged-in user",
"=": "=",
"≠": "≠",
">": ">",
@ -406,6 +456,7 @@ export default {
"Create calendar block": "Create calendar block",
"Create kanban block": "Create kanban block",
"Grouping field": "Grouping field",
"Single select and radio fields can be used as the grouping field": "Single select and radio fields can be used as the grouping field",
"Tab name": "Tab name",
"Current record blocks": "Current record blocks",
"Popup message": "Popup message",
@ -419,10 +470,13 @@ export default {
"General permissions": "General permissions",
"Global action permissions": "Global action permissions",
"General action permissions": "General action permissions",
"Plugin settings permissions":"Plugin settings permissions",
"Allow to desgin pages":"Allow to desgin pages",
"Allow to manage plugins":"Allow to manage plugins",
"Allow to configure plugins":"Allow to configure plugins",
"Plugin settings permissions": "Plugin settings permissions",
"Allow to desgin pages": "Allow to desgin pages",
"Allow to manage plugins": "Allow to manage plugins",
"Allow to configure plugins": "Allow to configure plugins",
"Allows to configure interface": "Allows to configure interface",
"Allows to install, activate, disable plugins": "Allows to install, activate, disable plugins",
"Allows to configure plugins": "Allows to configure plugins",
"Action display name": "Action display name",
"Allow": "Allow",
"Data scope": "Data scope",
@ -449,8 +503,11 @@ export default {
"Use the built-in static file server": "Use the built-in static file server",
"Local storage": "Local storage",
"Aliyun OSS": "Aliyun OSS",
"Tencent COS": "Tencent COS",
"Amazon S3": "Amazon S3",
"Tencent COS": "Tencent COS",
"Region": "Region",
"Bucket": "Bucket",
"Path": "Path",
"Unsaved changes": "Unsaved changes",
"Are you sure you don't want to save?": "Are you sure you don't want to save?",
"Dragging": "Dragging",
@ -462,6 +519,7 @@ export default {
"Dynamic value": "Dynamic value",
"Current user": "Current user",
"Current record": "Current record",
"Current time": "Current time",
"Popup close method": "Popup close method",
"Automatic close": "Automatic close",
"Manually close": "Manually close",
@ -489,29 +547,40 @@ export default {
"Record ID": "Record ID",
"User": "User",
"Field": "Field",
"Select": "Select",
"Select Field": "Select Field",
"Field value changes": "Field value changes",
"One to one (has one)": "One to one (has one)",
"One to one (belongs to)": "One to one (belongs to)",
"Use the same time zone (GMT) for all users": "Use the same time zone (GMT) for all users",
"Block title": "Block title",
"Edit block title": "Edit block title",
"Province/city/area name": "Province/city/area name",
"Field component": "Field component",
"Subtable": "Subtable",
"Subform": "Subform",
"Regular expression": "Pattern",
"Enabled languages": "Enabled languages",
"View all plugins": "View all plugins",
"Print": "Print",
'Single select and radio fields can be used as the grouping field': 'Single select and radio fields can be used as the grouping field',
'Sign up successfully, and automatically jump to the sign in page': 'Sign up successfully, and automatically jump to the sign in page',
"Done": "Done",
"Sign up successfully, and automatically jump to the sign in page": "Sign up successfully, and automatically jump to the sign in page",
"File manager": "File manager",
"ACL": "ACL",
"Collection manager": "Collection manager",
"Plugin manager": "Plugin manager",
"Local": "Local",
"Built-in": "Built-in",
"Marketplace": "Marketplace",
"Coming soon...": "Coming soon...",
"All plugin settings": "All plugin settings",
"Bookmark": "Bookmark",
"Manage all settings": "Manage all settings",
"Create inverse field in the target collection": "Create inverse field in the target collection",
"Inverse field name": "Inverse field name",
"Inverse field display name": "Inverse field display name",
"Bulk update": "Bulk update",
"After successful bulk update": "After successful bulk update",
"All": "All",
"Update selected data?": "Update selected data?",
"Update all data?": "Update all data?",
"Bulk edit": "Bulk edit",
"Data will be updated": "Data will be updated",
"Selected": "Selected",
"All": "All",
"Update selected data?": "Update selected data?",
"Update all data?": "Update all data?",
"Remains the same": "Remains the same",
"Changed to": "Changed to",
"Clear": "Clear",
@ -520,6 +589,13 @@ export default {
"Selector": "Selector",
"Inner": "Inner",
"Search and select collection": "Search and select collection",
'Please fill in the iframe URL': 'Please fill in the iframe URL',
'Fix block': 'Fix block'
}
"Please fill in the iframe URL": "Please fill in the iframe URL",
"Fix block": "Fix block",
"Plugin name": "Plugin name",
"Plugin tab name": "Plugin tab name",
"AutoGenId": "Auto-generated ID field",
"CreatedBy": "Recording a row's created user",
"UpdatedBy": "Recording a row's last updated user",
"CreatedAt": "Recording a row's created time ",
"UpdatedAt": "Recording a row's last updated user"
};

View File

@ -1,81 +1,75 @@
import antdEnUS from 'antd/lib/locale/en_US';
import antdJaJP from 'antd/lib/locale/ja_JP';
import antdRuRU from 'antd/lib/locale/ru_RU';
import antdTrTR from 'antd/lib/locale/tr_TR';
import antdZhCN from 'antd/lib/locale/zh_CN';
import enUS from './en_US';
import jaJP from './ja_JP';
import ruRU from './ru_RU';
import trTR from './tr_TR';
import zhCN from './zh_CN';
export type LocaleOptions = {
label: string;
moment: string;
antd: any;
resources?: any;
};
export { default as cron } from '../schema-component/antd/cron/locale';
export default {
'en-US': {
label: 'English',
// https://github.com/moment/moment/blob/develop/locale/en.js
moment: 'en',
// https://github.com/ant-design/ant-design/tree/master/components/locale/en_US
antd: antdEnUS,
resources: {
client: {
...enUS,
},
},
},
'ja-JP': {
label: '日本語',
// https://github.com/moment/moment/blob/develop/locale/ja.js
moment: 'ja',
// https://github.com/ant-design/ant-design/tree/master/components/locale/ja_JP
antd: antdJaJP,
resources: {
client: {
...jaJP,
},
},
},
'zh-CN': {
label: '简体中文',
// https://github.com/moment/moment/blob/develop/locale/zh-cn.js
moment: 'zh-cn',
// https://github.com/ant-design/ant-design/tree/master/components/locale/zh_CN
antd: antdZhCN,
// i18next
resources: {
client: {
...zhCN,
},
},
},
'ru-RU': {
label: 'Русский',
// https://github.com/moment/moment/blob/develop/locale/ru.js
moment: 'ru',
// https://github.com/ant-design/ant-design/tree/master/components/locale/ru_RU
antd: antdRuRU,
resources: {
client: {
...ruRU,
},
},
},
'tr-TR': {
label: 'Türkçe',
// https://github.com/moment/moment/blob/develop/locale/tr.js
moment: 'tr',
// https://github.com/ant-design/ant-design/tree/master/components/locale/tr_TR
antd: antdTrTR,
resources: {
client: {
...trTR,
},
},
},
} as Record<string, LocaleOptions>;
'ar-EG': { label: 'العربية' },
'az-AZ': { label: 'Azərbaycan dili' },
'bg-BG': { label: 'Български' },
'bn-BD': { label: 'Bengali' },
'by-BY': { label: 'Беларускі' },
'ca-ES': { label: 'Сatalà/Espanya' },
'cs-CZ': { label: 'Česky' },
'da-DK': { label: 'Dansk' },
'de-DE': { label: 'Deutsch' },
'el-GR': { label: 'Ελληνικά' },
'en-GB': { label: 'English(GB)' },
'en-US': { label: 'English' },
'es-ES': { label: 'Español' },
'et-EE': { label: 'Estonian (Eesti)' },
'fa-IR': { label: 'فارسی' },
'fi-FI': { label: 'Suomi' },
'fr-BE': { label: 'Français(BE)' },
'fr-CA': { label: 'Français(CA)' },
'fr-FR': { label: 'Français' },
'ga-IE': { label: 'Gaeilge' },
'gl-ES': { label: 'Galego' },
'he-IL': { label: 'עברית' },
'hi-IN': { label: 'हिन्दी' },
'hr-HR': { label: 'Hrvatski jezik' },
'hu-HU': { label: 'Magyar' },
'hy-AM': { label: 'Հայերեն' },
'id-ID': { label: 'Bahasa Indonesia' },
'is-IS': { label: 'Íslenska' },
'it-IT': { label: 'Italiano' },
'ja-JP': { label: '日本語' },
'ka-GE': { label: 'ქართული' },
'kk-KZ': { label: 'Қазақ тілі' },
'km-KH': { label: 'ភាសាខ្មែរ' },
// 'kmr-IQ': { label: 'kmr_IQ' },
'kn-IN': { label: 'ಕನ್ನಡ' },
'ko-KR': { label: '한국어' },
'ku-IQ': { label: 'کوردی' },
'lt-LT': { label: 'lietuvių' },
'lv-LV': { label: 'Latviešu valoda' },
'mk-MK': { label: 'македонски јазик' },
'ml-IN': { label: 'മലയാളം' },
'mn-MN': { label: 'Монгол хэл' },
'ms-MY': { label: 'بهاس ملايو' },
'nb-NO': { label: 'Norsk bokmål' },
'ne-NP': { label: 'नेपाली' },
'nl-BE': { label: 'Vlaams' },
'nl-NL': { label: 'Nederlands' },
'pl-PL': { label: 'Polski' },
'pt-BR': { label: 'Português brasileiro' },
'pt-PT': { label: 'Português' },
'ro-RO': { label: 'România' },
'ru-RU': { label: 'Русский' },
'si-LK': { label: 'සිංහල' },
'sk-SK': { label: 'Slovenčina' },
'sl-SI': { label: 'Slovenščina' },
'sr-RS': { label: 'српски језик' },
'sv-SE': { label: 'Svenska' },
'ta-IN': { label: 'Tamil' },
'th-TH': { label: 'ภาษาไทย' },
'tk-TK': { label: 'Turkmen' },
'tr-TR': { label: 'Türkçe' },
'uk-UA': { label: 'Українська' },
'ur-PK': { label: 'Oʻzbekcha' },
'vi-VN': { label: 'Tiếng Việt' },
'zh-CN': { label: '简体中文' },
'zh-HK': { label: '繁體中文(香港)' },
'zh-TW': { label: '繁體中文(台湾)' },
};

View File

@ -1,68 +1,54 @@
import React from 'react';
import { connect, mapProps, mapReadPretty } from '@formily/react';
import { Cron as ReactCron, CronProps } from 'react-js-cron';
import cronstrue from 'cronstrue';
import 'cronstrue/locales/zh_CN';
import { css } from '@emotion/css';
import { connect, mapReadPretty } from '@formily/react';
import cronstrue from 'cronstrue';
import React from 'react';
import { Cron as ReactCron, CronProps } from 'react-js-cron';
import { useAPIClient } from '../../../api-client';
import localeZhCN from './locale/zh-CN';
type ComposedCron = React.FC<CronProps> & {};
const ComponentLocales = {
'zh-CN': localeZhCN,
};
const ReadPrettyLocales = {
'en-US': 'en',
'zh-CN': 'zh_CN'
};
type ComposedCron = React.FC<CronProps> & {}
export const Cron: ComposedCron = connect(
(props: Exclude<CronProps, 'setValue'> & { onChange: (value: string) => void }) => {
const { onChange, ...rest } = props;
const locale = ComponentLocales[localStorage.getItem('NOCOBASE_LOCALE') || 'en-US'];
return (
<fieldset className={css`
.react-js-cron{
padding: .5em .5em 0 .5em;
const Input = (props: Exclude<CronProps, 'setValue'> & { onChange: (value: string) => void }) => {
const { onChange, ...rest } = props;
return (
<fieldset
className={css`
.react-js-cron {
padding: 0.5em 0.5em 0 0.5em;
border: 1px dashed #ccc;
.react-js-cron-field{
.react-js-cron-field {
flex-shrink: 0;
margin-bottom: .5em;
margin-bottom: 0.5em;
> span{
> span {
flex-shrink: 0;
margin: 0 .5em 0 0;
margin: 0 0.5em 0 0;
}
> .react-js-cron-select{
margin: 0 .5em 0 0;
> .react-js-cron-select {
margin: 0 0.5em 0 0;
}
}
`}>
<ReactCron
setValue={onChange}
locale={locale}
{...rest}
/>
</fieldset>
);
},
mapReadPretty((props) => {
const locale = ReadPrettyLocales[localStorage.getItem('NOCOBASE_LOCALE') || 'en-US'];
return props.value
? (
<span>
{cronstrue.toString(props.value, {
locale,
use24HourTimeFormat: true
})}
</span>
)
: null;
})
);
}
`}
>
<ReactCron setValue={onChange} locale={window['cronLocale']} {...rest} />
</fieldset>
);
};
const ReadPretty = (props) => {
const api = useAPIClient();
const locale = api.auth.getLocale();
return props.value ? (
<span>
{cronstrue.toString(props.value, {
locale,
use24HourTimeFormat: true,
})}
</span>
) : null;
};
export const Cron: ComposedCron = connect(Input, mapReadPretty(ReadPretty));
export default Cron;

View File

@ -0,0 +1,7 @@
import { DEFAULT_LOCALE_EN } from 'react-js-cron/dist/cjs/locale';
import zhCN from './zh-CN';
export default {
'zh-CN': zhCN,
'en-US': DEFAULT_LOCALE_EN,
};

View File

@ -13,7 +13,7 @@ import { ActionContext, SchemaComponent, useActionContext } from '../schema-comp
const langs = Object.keys(locale).map((lang) => {
return {
label: locale[lang].label,
label: `${locale[lang].label} (${lang})`,
value: lang,
};
});

View File

@ -16,7 +16,7 @@
"chalk": "^4.1.1",
"commander": "^9.2.0",
"find-package-json": "^1.2.0",
"i18next": "^21.6.0",
"i18next": "^22.4.9",
"koa": "^2.13.4",
"koa-bodyparser": "^4.3.0",
"koa-static": "^5.0.0",

View File

@ -2,12 +2,16 @@ export async function i18n(ctx, next) {
const i18n = ctx.app.i18n.cloneInstance({ initImmediate: false });
ctx.i18n = i18n;
ctx.t = i18n.t.bind(i18n);
const lng =
ctx.get('X-Locale') ||
(ctx.request.query.locale as string) ||
ctx.app.i18n.language ||
ctx.acceptsLanguages().shift() ||
'en-US';
ctx.getCurrentLocale = () => {
const lng =
ctx.get('X-Locale') ||
(ctx.request.query.locale as string) ||
ctx.app.i18n.language ||
ctx.acceptsLanguages().shift() ||
'en-US';
return lng;
};
const lng = ctx.getCurrentLocale();
if (lng !== '*' && lng) {
i18n.changeLanguage(lng);
}

View File

@ -0,0 +1,9 @@
export const getAntdLocale = (lang) => {
const lng = lang.replace('-', '_');
try {
require.resolve(`antd/lib/locale/${lng}`);
return require(`antd/lib/locale/${lng}`).default;
} catch (error) {
return require(`antd/lib/locale/en_US`).default;
}
};

View File

@ -0,0 +1,16 @@
export const getCronLocale = (lang: string) => {
let packageName = '@nocobase/client';
let locale = null;
try {
const file = `${packageName}/src/locale`;
require.resolve(file);
locale = require(file).cron?.[lang];
} catch (error) {
try {
const file = `${packageName}/lib/locale`;
require.resolve(file);
locale = require(file).cron?.[lang];
} catch (error) {}
}
return locale || require('react-js-cron/dist/cjs/locale').DEFAULT_LOCALE_EN;
};

View File

@ -0,0 +1,111 @@
const methods = [
'atX0SecondsPastTheMinuteGt20',
'atX0MinutesPastTheHourGt20',
'commaMonthX0ThroughMonthX1',
'commaYearX0ThroughYearX1',
'use24HourTimeFormatByDefault',
'anErrorOccuredWhenGeneratingTheExpressionD',
'everyMinute',
'everyHour',
'atSpace',
'everyMinuteBetweenX0AndX1',
'at',
'spaceAnd',
'everySecond',
'everyX0Seconds',
'secondsX0ThroughX1PastTheMinute',
'atX0SecondsPastTheMinute',
'everyX0Minutes',
'minutesX0ThroughX1PastTheHour',
'atX0MinutesPastTheHour',
'everyX0Hours',
'betweenX0AndX1',
'atX0',
'commaEveryDay',
'commaEveryX0DaysOfTheWeek',
'commaX0ThroughX1',
'commaAndX0ThroughX1',
'first',
'second',
'third',
'fourth',
'fifth',
'commaOnThe',
'spaceX0OfTheMonth',
'lastDay',
'commaOnTheLastX0OfTheMonth',
'commaOnlyOnX0',
'commaAndOnX0',
'commaEveryX0Months',
'commaOnlyInX0',
'commaOnTheLastDayOfTheMonth',
'commaOnTheLastWeekdayOfTheMonth',
'commaDaysBeforeTheLastDayOfTheMonth',
'firstWeekday',
'weekdayNearestDayX0',
'commaOnTheX0OfTheMonth',
'commaEveryX0Days',
'commaBetweenDayX0AndX1OfTheMonth',
'commaOnDayX0OfTheMonth',
'commaEveryHour',
'commaEveryX0Years',
'commaStartingX0',
'daysOfTheWeek',
'monthsOfTheYear',
];
const langs = {
af: 'af',
ar: 'ar',
be: 'be',
ca: 'ca',
cs: 'cs',
da: 'da',
de: 'de',
'en-US': 'en',
es: 'es',
fa: 'fa',
fi: 'fi',
fr: 'fr',
he: 'he',
hu: 'hu',
id: 'id',
it: 'it',
'ja-JP': 'ja',
ko: 'ko',
nb: 'nb',
nl: 'nl',
pl: 'pl',
pt_BR: 'pt_BR',
pt_PT: 'pt_PT',
ro: 'ro',
'ru-RU': 'ru',
sk: 'sk',
sl: 'sl',
sv: 'sv',
sw: 'sw',
'th-TH': 'th',
'tr-TR': 'tr',
uk: 'uk',
'zh-CN': 'zh_CN',
'zh-TW': 'zh_TW',
};
export const getCronstrueLocale = (lang) => {
const lng = langs[lang] || 'en';
const Locale = require(`cronstrue/locales/${lng}`);
let locale;
if (Locale?.default) {
locale = Locale.default.locales[lng];
} else {
const L = Locale[lng];
locale = new L();
}
const items = {};
for (const method of methods) {
try {
items[method] = locale[method]();
} catch (error) {}
}
return items;
};

View File

@ -0,0 +1,3 @@
export default {
// 'zh-CN': require('./zh-CN.example.json'),
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,141 @@
const locales = {
af: 'af',
'ar-dz': 'ar-dz',
'ar-kw': 'ar-kw',
'ar-ly': 'ar-ly',
'ar-ma': 'ar-ma',
'ar-sa': 'ar-sa',
'ar-tn': 'ar-tn',
ar: 'ar',
az: 'az',
be: 'be',
bg: 'bg',
bm: 'bm',
'bn-bd': 'bn-bd',
bn: 'bn',
bo: 'bo',
br: 'br',
bs: 'bs',
ca: 'ca',
cs: 'cs',
cv: 'cv',
cy: 'cy',
da: 'da',
'de-at': 'de-at',
'de-ch': 'de-ch',
de: 'de',
dv: 'dv',
el: 'el',
'en-au': 'en-au',
'en-ca': 'en-ca',
'en-gb': 'en-gb',
'en-ie': 'en-ie',
'en-il': 'en-il',
'en-in': 'en-in',
'en-nz': 'en-nz',
'en-sg': 'en-sg',
eo: 'eo',
'es-do': 'es-do',
'es-mx': 'es-mx',
'es-us': 'es-us',
es: 'es',
et: 'et',
eu: 'eu',
fa: 'fa',
fi: 'fi',
fil: 'fil',
fo: 'fo',
'fr-ca': 'fr-ca',
'fr-ch': 'fr-ch',
fr: 'fr',
fy: 'fy',
ga: 'ga',
gd: 'gd',
gl: 'gl',
'gom-deva': 'gom-deva',
'gom-latn': 'gom-latn',
gu: 'gu',
he: 'he',
hi: 'hi',
hr: 'hr',
hu: 'hu',
'hy-am': 'hy-am',
id: 'id',
is: 'is',
'it-ch': 'it-ch',
it: 'it',
'ja-JP': 'ja',
jv: 'jv',
ka: 'ka',
kk: 'kk',
km: 'km',
kn: 'kn',
ko: 'ko',
ku: 'ku',
ky: 'ky',
lb: 'lb',
lo: 'lo',
lt: 'lt',
lv: 'lv',
me: 'me',
mi: 'mi',
mk: 'mk',
ml: 'ml',
mn: 'mn',
mr: 'mr',
'ms-my': 'ms-my',
ms: 'ms',
mt: 'mt',
my: 'my',
nb: 'nb',
ne: 'ne',
'nl-be': 'nl-be',
nl: 'nl',
nn: 'nn',
'oc-lnc': 'oc-lnc',
'pa-in': 'pa-in',
pl: 'pl',
'pt-br': 'pt-br',
pt: 'pt',
ro: 'ro',
'ru-RU': 'ru',
sd: 'sd',
se: 'se',
si: 'si',
sk: 'sk',
sl: 'sl',
sq: 'sq',
'sr-cyrl': 'sr-cyrl',
sr: 'sr',
ss: 'ss',
sv: 'sv',
sw: 'sw',
ta: 'ta',
te: 'te',
tet: 'tet',
tg: 'tg',
'th-TH': 'th',
tk: 'tk',
'tl-ph': 'tl-ph',
tlh: 'tlh',
'tr-TR': 'tr',
tzl: 'tzl',
'tzm-latn': 'tzm-latn',
tzm: 'tzm',
'ug-cn': 'ug-cn',
uk: 'uk',
ur: 'ur',
'uz-latn': 'uz-latn',
uz: 'uz',
vi: 'vi',
'x-pseudo': 'x-pseudo',
yo: 'yo',
'zh-CN': 'zh-cn',
'zh-hk': 'zh-hk',
'zh-mo': 'zh-mo',
'zh-TW': 'zh-tw',
};
export const getMomentLocale = (lang: string) => {
return locales[lang] || 'en';
};

View File

@ -0,0 +1,69 @@
import { PluginManager } from '@nocobase/server';
const arr2obj = (items: any[]) => {
const obj = {};
for (const item of items) {
Object.assign(obj, item);
}
return obj;
};
const getResource = (packageName: string, lang: string) => {
let resources = [];
const prefixes = ['src', 'lib'];
const localeKeys = ['locale', 'client/locale', 'server/locale'];
for (const prefix of prefixes) {
for (const localeKey of localeKeys) {
try {
const file = `${packageName}/${prefix}/${localeKey}/${lang}`;
require.resolve(file);
const resource = require(file).default;
resources.push(resource);
} catch (error) {}
}
if (resources.length) {
break;
}
}
if (resources.length === 0 && lang.replace('-', '_') !== lang) {
return getResource(packageName, lang.replace('-', '_'));
}
return arr2obj(resources);
};
export const getResourceLocale = async (lang: string, db: any) => {
const resources = {};
const res = getResource('@nocobase/client', lang);
const defaults = getResource('@nocobase/client', 'zh-CN');
for (const key in defaults) {
if (Object.prototype.hasOwnProperty.call(defaults, key)) {
defaults[key] = key;
}
}
if (res) {
resources['client'] = { ...defaults, ...res };
} else {
resources['client'] = defaults;
}
const plugins = await db.getRepository('applicationPlugins').find({
filter: {
'name.$ne': 'client',
},
});
for (const plugin of plugins) {
const packageName = PluginManager.getPackageName(plugin.get('name'));
const res = getResource(packageName, lang);
const defaults = getResource(packageName, 'zh-CN');
for (const key in defaults) {
if (Object.prototype.hasOwnProperty.call(defaults, key)) {
defaults[key] = key;
}
}
if (res) {
resources[plugin.get('name')] = { ...defaults, ...res };
} else {
resources['client'] = defaults;
}
}
return resources;
};

View File

@ -1,7 +1,13 @@
import { Plugin, PluginManager } from '@nocobase/server';
import send from 'koa-send';
import serve from 'koa-static';
import isEmpty from 'lodash/isEmpty';
import { isAbsolute, resolve } from 'path';
import { getAntdLocale } from './antd';
import { getCronLocale } from './cron';
import { getCronstrueLocale } from './cronstrue';
import { getMomentLocale } from './moment-locale';
import { getResourceLocale } from './resource';
export class ClientPlugin extends Plugin {
async beforeLoad() {
@ -23,6 +29,7 @@ export class ClientPlugin extends Plugin {
this.app.acl.allow('app', 'getPlugins');
this.app.acl.allow('plugins', 'getPinned', 'loggedIn');
const dialect = this.app.db.sequelize.getDialect();
const locales = require('./locale').default;
this.app.resource({
name: 'app',
actions: {
@ -53,8 +60,28 @@ export class ClientPlugin extends Plugin {
if (enabledLanguages.includes(currentUser?.appLang)) {
lang = currentUser?.appLang;
}
if (ctx.request.query.locale) {
lang = ctx.request.query.locale;
}
if (isEmpty(locales[lang])) {
locales[lang] = {};
}
if (isEmpty(locales[lang].resources)) {
locales[lang].resources = await getResourceLocale(lang, ctx.db);
}
if (isEmpty(locales[lang].antd)) {
locales[lang].antd = getAntdLocale(lang);
}
if (isEmpty(locales[lang].cronstrue)) {
locales[lang].cronstrue = getCronstrueLocale(lang);
}
if (isEmpty(locales[lang].cron)) {
locales[lang].cron = getCronLocale(lang);
}
ctx.body = {
lang,
moment: getMomentLocale(lang),
...locales[lang],
};
await next();
},

View File

@ -1,4 +1,6 @@
export default {
'unique violation': '{{field}} must be unique',
'notNull Violation': '{{field}} cannot be null',
"unique violation": "{{field}} must be unique",
"notNull violation": "notNull violation",
"Validation error": "{{field}} validation error",
"notNull Violation": "{{field}} cannot be null"
};

View File

@ -1,17 +1,16 @@
import { i18n, PluginManagerContext, RouteSwitchContext, SettingsCenterContext, SettingsCenterProvider } from '@nocobase/client';
import { PluginManagerContext, RouteSwitchContext, SettingsCenterContext, SettingsCenterProvider } from '@nocobase/client';
import React, { useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { GraphCollectionPane, GraphCollectionShortcut } from './GraphCollectionShortcut';
import { enUS, jaJP, zhCN } from './locale';
import { useGCMTranslation } from './utils';
export const GraphCollectionProvider = React.memo((props) => {
const ctx = useContext(PluginManagerContext);
const { routes, components, ...others } = useContext(RouteSwitchContext);
i18n.addResources('en-US', 'graphPositions', enUS);
i18n.addResources('ja-JP', 'graphPositions', jaJP);
i18n.addResources('zh-CN', 'graphPositions', zhCN);
const { t } = useTranslation('graphPositions');
// i18n.addResources('en-US', 'graphPositions', enUS);
// i18n.addResources('ja-JP', 'graphPositions', jaJP);
// i18n.addResources('zh-CN', 'graphPositions', zhCN);
const { t } = useGCMTranslation();
const items = useContext(SettingsCenterContext);
items['collection-manager']['tabs']['graph'] = {

View File

@ -1,12 +1,12 @@
import { DeleteOutlined, PartitionOutlined } from '@ant-design/icons';
import { css } from '@emotion/css';
import { uid } from '@formily/shared';
import { PluginManager, SchemaComponent, useActionContext, useRequest } from '@nocobase/client';
import React, { useEffect } from 'react';
import { css } from '@emotion/css';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import { useCreateActionAndRefreshCM } from './action-hooks';
import { GraphDrawPage } from './GraphDrawPage';
import { useGCMTranslation } from './utils';
const useCollectionValues = (options) => {
const { visible } = useActionContext();
@ -144,7 +144,7 @@ export const GraphCollectionPane = () => {
};
export const GraphCollectionShortcut = () => {
const { t } = useTranslation('graphPositions');
const { t } = useGCMTranslation();
const history = useHistory();
return (
<PluginManager.Toolbar.Item

View File

@ -1,10 +1,8 @@
import {
ApartmentOutlined,
FullscreenExitOutlined,
FullscreenOutlined,
MenuOutlined,
ShareAltOutlined,
LineHeightOutlined,
FullscreenOutlined, LineHeightOutlined, MenuOutlined,
ShareAltOutlined
} from '@ant-design/icons';
import { Graph } from '@antv/x6';
import '@antv/x6-react-shape';
@ -12,30 +10,25 @@ import { css, cx } from '@emotion/css';
import { SchemaOptionsContext } from '@formily/react';
import {
APIClientProvider,
collection,
CollectionManagerProvider,
collection, CollectionManagerContext, CollectionManagerProvider,
CurrentAppInfoContext,
SchemaComponent,
SchemaComponentOptions,
useAPIClient,
SchemaComponentOptions, Select, useAPIClient,
useCollectionManager,
useCompile,
useCurrentAppInfo,
Select,
CollectionManagerContext,
useCurrentAppInfo
} from '@nocobase/client';
import { useFullscreen } from 'ahooks';
import { Button, Input, Layout, Menu, Popover, Tooltip, Switch } from 'antd';
import { Button, Input, Layout, Menu, Popover, Switch, Tooltip } from 'antd';
import dagre from 'dagre';
import { drop, groupBy, last, maxBy, minBy, take, debounce } from 'lodash';
import { drop, groupBy, last, maxBy, minBy, take } from 'lodash';
import React, { createContext, forwardRef, useContext, useEffect, useLayoutEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useAsyncDataSource, useCreateActionAndRefreshCM } from './action-hooks';
import { AddCollectionAction } from './components/AddCollectionAction';
import Entity from './components/Entity';
import { collectionListClass, graphCollectionContainerClass } from './style';
import { formatData, getDiffEdge, getDiffNode, getInheritCollections, getChildrenCollections } from './utils';
import { SimpleNodeView } from './components/ViewNode';
import { collectionListClass, graphCollectionContainerClass } from './style';
import { formatData, getChildrenCollections, getDiffEdge, getDiffNode, getInheritCollections, useGCMTranslation } from './utils';
const LINE_HEIGHT = 40;
const NODE_WIDTH = 250;
@ -360,7 +353,7 @@ export const GraphDrawPage = React.memo(() => {
const ctx = useContext(CollectionManagerContext);
const api = useAPIClient();
const compile = useCompile();
const { t } = useTranslation('graphPositions');
const { t } = useGCMTranslation();
const [collectionData, setCollectionData] = useState<any>([]);
const [collectionList, setCollectionList] = useState<any>([]);
const { refreshCM } = useCollectionManager();

View File

@ -1,14 +1,16 @@
import { DeleteOutlined, EditOutlined, DownOutlined, UpOutlined } from '@ant-design/icons';
import { DeleteOutlined, DownOutlined, EditOutlined, UpOutlined } from '@ant-design/icons';
import '@antv/x6-react-shape';
import { css, cx } from '@emotion/css';
import { uid } from '@formily/shared';
import {
Action,
Checkbox,
collection,
CollectionField,
CollectionProvider,
Form,
FormItem,
Formula,
Grid,
Input,
InputNumber,
@ -19,31 +21,29 @@ import {
Select,
useCollectionManager,
useCompile,
Formula,
useRecord,
collection,
useCurrentAppInfo,
useRecord
} from '@nocobase/client';
import { useTranslation } from 'react-i18next';
import { Dropdown, Popover, Tag, Badge } from 'antd';
import React, { useRef, useState } from 'react';
import { Badge, Dropdown, Popover, Tag } from 'antd';
import { groupBy } from 'lodash';
import React, { useRef, useState } from 'react';
import {
useAsyncDataSource,
useCancelAction,
useDestroyActionAndRefreshCM,
useDestroyFieldActionAndRefreshCM,
useUpdateCollectionActionAndRefreshCM,
useValuesFromRecord,
useValuesFromRecord
} from '../action-hooks';
import { collectiionPopoverClass, entityContainer, headClass, tableBtnClass, tableNameClass } from '../style';
import { FieldSummary } from './FieldSummary';
import { useGCMTranslation } from '../utils';
import { AddFieldAction } from './AddFieldAction';
import { CollectionNodeProvder } from './CollectionNodeProvder';
import { EditCollectionAction } from './EditCollectionAction';
import { EditFieldAction } from './EditFieldAction';
import { FieldSummary } from './FieldSummary';
import { OverrideFieldAction } from './OverrideFieldAction';
import { ViewFieldAction } from './ViewFieldAction';
import { EditCollectionAction } from './EditCollectionAction';
const Entity: React.FC<{
node?: Node | any;
@ -166,7 +166,7 @@ const PortsCom = React.memo<any>(({ targetGraph, collectionData, setTargetNode,
},
} = node;
const [collapse, setCollapse] = useState(false);
const { t } = useTranslation('graphPositions');
const { t } = useGCMTranslation();
const compile = useCompile();
const portsData = groupBy(ports.items, (v) => {
if (

View File

@ -1,8 +1,15 @@
import { uniqBy, groupBy, reduce, uniq } from 'lodash';
import { groupBy, reduce, uniq, uniqBy } from 'lodash';
import { useTranslation } from 'react-i18next';
const shape = {
ER: 'er-rect',
EDGE: 'edge',
};
export const useGCMTranslation = () => {
return useTranslation('graph-collection-manager');
};
export const getInheritCollections = (collections, name) => {
const parents = [];
const getParents = (name) => {

View File

@ -1,9 +1,6 @@
import { i18n } from '@nocobase/client';
import { NAMESPACE } from './constants';
import { enUS, zhCN } from './locale';
i18n.addResources('zh-CN', NAMESPACE, zhCN);
i18n.addResources('en-US', NAMESPACE, enUS);
// i18n.addResources('zh-CN', NAMESPACE, zhCN);
// i18n.addResources('en-US', NAMESPACE, enUS);
export * from './ImportActionInitializer';
export * from './ImportDesigner';
@ -11,3 +8,4 @@ export * from './ImportInitializerProvider';
export * from './ImportPluginProvider';
export { ImportPluginProvider as default } from './ImportPluginProvider';
export * from './useImportAction';

View File

@ -1,23 +1,27 @@
export default {
'Only one file is allowed to be uploaded': 'Only one file is allowed to be uploaded',
'File size cannot exceed 10M': 'File size cannot exceed 10M',
'Please upload the file of Excel': 'Please upload the file of Excel',
'Import Data': 'Import Data',
'Start import': 'Start import',
'Import explain': 'Guide',
'Download template': 'Download template',
'Step 1: Download template': 'Step 1: Download template',
'Step 2: Upload Excel': 'Step 2: Upload Excel',
'Download tip':
'- Download the template and fill in the data according to the format \r\n - Import only the first worksheet \r\n - Support single import of up to 10,000 rows of data \r\n - Do not change the header of the template to prevent import failure',
'Upload placeholder': 'Drag and drop the file here or click to upload, file size should not exceed 10M',
'Excel data importing': 'Excel data importing',
'Import done, total success have {{successCount}} , total failure have {{failureCount}}':
'Import is complete, with a total of {{successCount}} successful and {{failureCount}} failed',
'To download the failure data': 'To download the failure data',
'Add importable field': 'Add importable field',
Done: 'Done',
Yes: 'Yes',
No: 'No',
'Field {{fieldName}} does not exist': 'Field {{fieldName}} does not exist',
"Only one file is allowed to be uploaded": "Only one file is allowed to be uploaded",
"File size cannot exceed 10M": "File size cannot exceed 10M",
"Please upload the file of Excel": "Please upload the file of Excel",
"Import Data": "Import Data",
"Start import": "Start import",
"Import explain": "Guide",
"Download template": "Download template",
"Step 1: Download template": "Step 1: Download template",
"Step 2: Upload Excel": "Step 2: Upload Excel",
"Download tip": "- Download the template and fill in the data according to the format \r\n - Import only the first worksheet \r\n - Support single import of up to 10,000 rows of data \r\n - Do not change the header of the template to prevent import failure",
"Upload placeholder": "Drag and drop the file here or click to upload, file size should not exceed 10M",
"Excel data importing": "Excel data importing",
"Import done, total success have {{successCount}} , total failure have {{failureCount}}": "Import is complete, with a total of {{successCount}} successful and {{failureCount}} failed",
"To download the failure data": "To download the failure data",
"Add importable field": "Add importable field",
"Done": "Done",
"Yes": "Yes",
"No": "No",
"Field {{fieldName}} does not exist": "Field {{fieldName}} does not exist",
"can not find value": "can not find value",
"password is empty": "password is empty",
"Incorrect time format": "Incorrect time format",
"Incorrect date format": "Incorrect date format",
"Incorrect email format": "Incorrect email format",
"Illegal percentage format": "Illegal percentage format"
};

View File

@ -1,17 +1,16 @@
import React from 'react';
import { useEffect, useRef, useState } from 'react';
import AMapLoader from '@amap/amap-jsapi-loader';
import '@amap/amap-jsapi-types';
import { SyncOutlined } from '@ant-design/icons';
import { css } from '@emotion/css';
import { useFieldSchema } from '@formily/react';
import { useCollection } from '@nocobase/client';
import { css } from '@emotion/css';
import { Alert, Button, Modal } from 'antd';
import { useMapTranslation } from '../locales';
import Search from './Search';
import { useMemoizedFn } from 'ahooks';
import { useMapConfiguration } from '../hooks';
import { Alert, Button, Modal } from 'antd';
import React, { useEffect, useRef, useState } from 'react';
import { useHistory } from 'react-router';
import { SyncOutlined } from '@ant-design/icons';
import { useMapConfiguration } from '../hooks';
import { useMapTranslation } from '../locale';
import Search from './Search';
interface AMapComponentProps {
accessKey: string;

View File

@ -1,10 +1,10 @@
import { useAPIClient, useCompile, useRequest } from '@nocobase/client';
import { useAPIClient, useCompile } from '@nocobase/client';
import { useBoolean } from 'ahooks';
import { Form, Input, Tabs, Button, Card, message } from 'antd';
import React, { useMemo, useEffect } from 'react';
import { Button, Card, Form, Input, message, Tabs } from 'antd';
import React, { useEffect, useMemo } from 'react';
import { MapTypes } from '../constants';
import { MapConfigurationResourceKey, useMapConfiguration } from '../hooks';
import { useMapTranslation } from '../locales';
import { useMapTranslation } from '../locale';
const AMapConfiguration = ({ type }) => {
const { t } = useMapTranslation();

View File

@ -6,11 +6,11 @@ import {
useCollection,
useCollectionManager,
useDesignable,
useFormBlockContext,
useFormBlockContext
} from '@nocobase/client';
import _ from 'lodash';
import set from 'lodash/set';
import React from 'react';
import { useMapTranslation } from '../locales';
import { useMapTranslation } from '../locale';
const Designer = () => {
const { getCollectionJoinField } = useCollectionManager();
@ -230,7 +230,7 @@ const Designer = () => {
}
onSubmit={({ zoom }) => {
if (zoom) {
_.set(fieldSchema, 'x-component-props.zoom', zoom);
set(fieldSchema, 'x-component-props.zoom', zoom);
Object.assign(field.componentProps, fieldSchema['x-component-props']);
dn.emit('patch', {

View File

@ -1,8 +1,8 @@
import { message, Select } from 'antd';
import { css } from '@emotion/css';
import React, { useEffect, useRef, useState } from 'react';
import { useDebounceFn } from 'ahooks';
import { useMapTranslation } from '../locales';
import { message, Select } from 'antd';
import React, { useEffect, useRef, useState } from 'react';
import { useMapTranslation } from '../locale';
interface SearchProps {
aMap: any;

View File

@ -1,4 +1,4 @@
import { generateNTemplate } from "./locales";
import { generateNTemplate } from "./locale";
export const MapTypes = [
{ label: generateNTemplate('AMap'), value: 'amap' },

View File

@ -1,5 +1,5 @@
import { IField } from '@nocobase/client';
import { generateNTemplate } from '../locales';
import { generateNTemplate } from '../locale';
import { commonSchema } from './schema';
export const circle: IField = {

View File

@ -1,5 +1,5 @@
import { IField } from '@nocobase/client';
import { generateNTemplate } from '../locales';
import { generateNTemplate } from '../locale';
import { commonSchema } from './schema';
export const lineString: IField = {

View File

@ -1,6 +1,5 @@
import { ISchema } from '@formily/react';
import { IField } from '@nocobase/client';
import { generateNTemplate } from '../locales';
import { generateNTemplate } from '../locale';
import { commonSchema } from './schema';
export const point: IField = {

View File

@ -1,5 +1,5 @@
import { IField } from '@nocobase/client';
import { generateNTemplate } from '../locales';
import { generateNTemplate } from '../locale';
import { commonSchema } from './schema';
export const polygon: IField = {

View File

@ -1,7 +1,7 @@
import { ISchema } from '@formily/react';
import { interfacesProperties } from '@nocobase/client';
import { MapTypes } from '../constants';
import { generateNTemplate } from '../locales';
import { generateNTemplate } from '../locale';
const { defaultProps } = interfacesProperties;

View File

@ -9,7 +9,7 @@ import Configuration from './components/Configuration';
import Map from './components/Map';
import { interfaces } from './fields';
import { Initialize } from './initialize';
import { useMapTranslation } from './locales';
import { useMapTranslation } from './locale';
export default React.memo((props) => {
const ctx = useContext(CollectionManagerContext);

View File

@ -1,9 +1,8 @@
import React from 'react';
import { registerField, registerGroup, useCurrentAppInfo } from '@nocobase/client';
import { generateNTemplate } from './locales';
import './locales';
import React, { useEffect } from 'react';
import { fields } from './fields';
import { useEffect } from 'react';
import './locale';
import { generateNTemplate } from './locale';
export const useRegisterInterface = () => {
const { data } = useCurrentAppInfo() || {};

View File

@ -1,13 +1,11 @@
import { i18n } from '@nocobase/client';
import { useTranslation } from 'react-i18next';
import enUS from './en-US';
import zhCN from './zh-CN';
export const NAMESPACE = 'map';
i18n.addResources('zh-CN', NAMESPACE, zhCN);
i18n.addResources('en-US', NAMESPACE, enUS);
// i18n.addResources('zh-CN', NAMESPACE, zhCN);
// i18n.addResources('en-US', NAMESPACE, enUS);
export function lang(key: string) {
return i18n.t(key, { ns: NAMESPACE });

View File

@ -1,19 +1,14 @@
import { i18n } from '@nocobase/client';
import { useTranslation } from 'react-i18next';
import enUS from './en-US';
import jaJP from './ja-JP';
import ruRU from './ru-RU';
import trTR from './tr-TR';
import zhCN from './zh-CN';
export const NAMESPACE = 'oidc';
i18n.addResources('zh-CN', NAMESPACE, zhCN);
i18n.addResources('en-US', NAMESPACE, enUS);
i18n.addResources('ja-JP', NAMESPACE, jaJP);
i18n.addResources('ru-RU', NAMESPACE, ruRU);
i18n.addResources('tr-TR', NAMESPACE, trTR);
// i18n.addResources('zh-CN', NAMESPACE, zhCN);
// i18n.addResources('en-US', NAMESPACE, enUS);
// i18n.addResources('ja-JP', NAMESPACE, jaJP);
// i18n.addResources('ru-RU', NAMESPACE, ruRU);
// i18n.addResources('tr-TR', NAMESPACE, trTR);
export function lang(key: string) {
return i18n.t(key, { ns: NAMESPACE });

View File

@ -1,19 +1,14 @@
import { i18n } from '@nocobase/client';
import { useTranslation } from 'react-i18next';
import enUS from './en-US';
import jaJP from './ja-JP';
import ruRU from './ru-RU';
import trTR from './tr-TR';
import zhCN from './zh-CN';
export const NAMESPACE = 'saml';
i18n.addResources('zh-CN', NAMESPACE, zhCN);
i18n.addResources('en-US', NAMESPACE, enUS);
i18n.addResources('ja-JP', NAMESPACE, jaJP);
i18n.addResources('ru-RU', NAMESPACE, ruRU);
i18n.addResources('tr-TR', NAMESPACE, trTR);
// i18n.addResources('zh-CN', NAMESPACE, zhCN);
// i18n.addResources('en-US', NAMESPACE, enUS);
// i18n.addResources('ja-JP', NAMESPACE, jaJP);
// i18n.addResources('ru-RU', NAMESPACE, ruRU);
// i18n.addResources('tr-TR', NAMESPACE, trTR);
export function lang(key: string) {
return i18n.t(key, { ns: NAMESPACE });

View File

@ -0,0 +1,24 @@
export default {
"Sequence": "Sequence",
"Sequence rules": "Sequence rules",
"Add rule": "Add rule",
"Inputable": "Inputable",
"Match rules": "Match rules",
"Type": "Type",
"Autoincrement": "Autoincrement",
"Fixed text": "Fixed text",
"Text content": "Text content",
"Rule content": "Rule content",
"{{value}} Digits": "{{value}} Digits",
"Digits": "Digits",
"Start from": "Start from",
"Starts from {{value}}": "Starts from {{value}}",
"Reset cycle": "Reset cycle",
"No reset": "No reset",
"Daily": "Daily",
"Every Monday": "Every Monday",
"Monthly": "Monthly",
"Yearly": "Yearly",
"Operations": "Operations",
"Customize": "Customize"
};

View File

@ -1,11 +1,10 @@
import { useTranslation } from 'react-i18next';
import { i18n } from '@nocobase/client';
import { useTranslation } from 'react-i18next';
import zhCN from './zh-CN';
export const NAMESPACE = 'sequence_field';
export const NAMESPACE = 'sequence-field';
i18n.addResources('zh-CN', NAMESPACE, zhCN);
// i18n.addResources('zh-CN', NAMESPACE, zhCN);
export function lang(key: string, options = {}) {
return i18n.t(key, { ...options, ns: NAMESPACE });

View File

@ -1,19 +1,14 @@
import { i18n } from '@nocobase/client';
import { useTranslation } from 'react-i18next';
import enUS from './en-US';
import jaJP from './ja-JP';
import ruRU from './ru-RU';
import trTR from './tr-TR';
import zhCN from './zh-CN';
export const NAMESPACE = 'snapshot-field';
i18n.addResources('zh-CN', NAMESPACE, zhCN);
i18n.addResources('en-US', NAMESPACE, enUS);
i18n.addResources('ja-JP', NAMESPACE, jaJP);
i18n.addResources('ru-RU', NAMESPACE, ruRU);
i18n.addResources('tr-TR', NAMESPACE, trTR);
// i18n.addResources('zh-CN', NAMESPACE, zhCN);
// i18n.addResources('en-US', NAMESPACE, enUS);
// i18n.addResources('ja-JP', NAMESPACE, jaJP);
// i18n.addResources('ru-RU', NAMESPACE, ruRU);
// i18n.addResources('tr-TR', NAMESPACE, trTR);
export function lang(key: string) {
return i18n.t(key, { ns: NAMESPACE });

View File

@ -1,4 +1,8 @@
export default {
'Please fill in your email address': 'Please fill in your email address',
'The password is incorrect, please re-enter': 'The password is incorrect, please re-enter',
"The email is incorrect, please re-enter": "The email is incorrect, please re-enter",
"Please fill in your email address": "Please fill in your email address",
"The password is incorrect, please re-enter": "The password is incorrect, please re-enter",
"Not a valid cellphone number, please re-enter": "Not a valid cellphone number, please re-enter",
"The phone number has been registered, please login directly": "The phone number has been registered, please login directly",
"The phone number is not registered, please register first": "The phone number is not registered, please register first"
};

View File

@ -1,11 +1,10 @@
import { useTranslation } from 'react-i18next';
import { i18n } from '@nocobase/client';
import { useTranslation } from 'react-i18next';
import zhCN from './zh-CN';
export const NAMESPACE = 'verification';
i18n.addResources('zh-CN', NAMESPACE, zhCN);
// i18n.addResources('zh-CN', NAMESPACE, zhCN);
export function lang(key: string) {
return i18n.t(key, { ns: NAMESPACE });

View File

@ -1,14 +1,21 @@
export default {
"Workflow": "Workflow",
"Execution history": "Execution history",
"Executed": "Executed",
"Trigger type": "Trigger type",
"Status": "Status",
"On": "On",
"Off": "Off",
"Version": "Version",
"Copy to new version": "Copy to new version",
"Duplicate": "Duplicate",
"Loading": "Loading",
"Load failed": "Load failed",
"Trigger": "Trigger",
"Trigger variables": "Trigger variables",
"Trigger data": "Trigger data",
"Trigger time": "Trigger time",
"Triggered at": "Triggered at",
"Collection event": "Collection event",
"Trigger on": "Trigger on",
"After record added": "After record added",
@ -24,6 +31,7 @@ export default {
"Based on date field of collection": "Based on date field of collection",
"Starts on": "Starts on",
"Ends on": "Ends on",
"No end": "No end",
"Exactly at": "Exactly at",
"Repeat mode": "Repeat mode",
"Repeat limit": "Repeat limit",
@ -45,36 +53,51 @@ export default {
"By custom date": "By custom date",
"Advanced": "Advanced",
"End": "End",
"Trigger variables": "Trigger variables",
"Node result": "Node result",
"Constant": "Constant",
"Null": "Null",
"Boolean": "Boolean",
"String": "String",
"Calculator": "Calculator",
"Arithmetic calculation": "Arithmetic calculation",
"String operation": "String operation",
"Executed at": "Executed at",
"Queueing": "Queueing",
"On going": "On going",
"Succeeded": "Succeeded",
"Failed": "Failed",
"Pending": "Pending",
"Canceled": "Canceled",
"This node contains branches, deleting will also be preformed to them, are you sure?": "This node contains branches, deleting will also be preformed to them, are you sure?",
"Control": "Control",
"Collection operations": "Collection operations",
"Extended types": "Extended types",
"Node type": "Node type",
"Calculation": "Calculation",
"Configure calculation": "Configure calculation",
"Calculation result": "Calculation result",
"True": "True",
"False": "False",
"concat": "concat",
"Condition": "Condition",
"Mode": "Mode",
"Continue when \"Yes\"": "Continue when \"Yes\"",
"Branch into \"Yes\" and \"No\"": "Branch into \"Yes\" and \"No\"",
"Conditions": "Conditions",
"Parallel branch": "Parallel branch",
"Add branch": "Add branch",
"All succeeded": "All succeeded",
"Any succeeded": "Any succeeded",
"Any succeeded or failed": "Any succeeded or failed",
"Continue after all branches succeeded": "Continue after all branches succeeded",
"Continue after any branch succeeded": "Continue after any branch succeeded",
"Continue after any branch succeeded, or exit after any branch failed": "Continue after any branch succeeded, or exit after any branch failed",
"Delay": "Delay",
"Duration": "Duration",
"End Status": "End Status",
"Select status": "Select status",
"Succeed and continue": "Succeed and continue",
"Fail and exit": "Fail and exit",
"Create record": "Create record",
"Update record": "Update record",
"Query record": "Query record",
@ -84,22 +107,23 @@ export default {
"Fields that are not assigned a value will be set to the default value, and those that do not have a default value are set to null.": "Fields that are not assigned a value will be set to the default value, and those that do not have a default value are set to null.",
"Trigger in executed workflow cannot be modified": "Trigger in executed workflow cannot be modified",
"Node in executed workflow cannot be modified": "Node in executed workflow cannot be modified",
'HTTP request': 'HTTP request',
'URL': 'URL',
'You can use the above available variables in URL.': 'You can use the above available variables in URL.',
'Request headers': 'Request headers',
'Name(e.g. Content-Type)': 'Name(e.g. Content-Type)',
'Value(e.g. Application/json)': 'Value(e.g. Application/json)',
'Add request header': 'Add request header',
'HTTP method': 'HTTP method',
'Request data': 'Request data',
'Input request data': 'Input request data',
'You can use the above available variables in request data.': 'You can use the above available variables in request data.',
'Copy success!': 'Copy success!',
'Copy variable output template statement': 'Copy variable output template statement',
'Default headers is Content-Type: application/json': 'Default headers is Content-Type: application/json',
'Ignore fail request and continue workflow': 'Ignore fail request and continue workflow',
'Syntax see': 'Syntax see',
'Show available variable tool': 'Show available variable tool'
"Can not delete": "Can not delete",
"The result of this node has been referenced by other nodes ({{nodes}}), please remove the usage before deleting.": "The result of this node has been referenced by other nodes ({{nodes}}), please remove the usage before deleting.",
"HTTP request": "HTTP request",
"URL": "URL",
"You can use the above available variables in URL.": "You can use the above available variables in URL.",
"Request headers": "Request headers",
"Name(e.g. Content-Type)": "Name(e.g. Content-Type)",
"Value(e.g. Application/json)": "Value(e.g. Application/json)",
"Add request header": "Add request header",
"HTTP method": "HTTP method",
"Request data": "Request data",
"Input request data": "Input request data",
"You can use the above available variables in request data.": "You can use the above available variables in request data.",
"Copy success!": "Copy success!",
"Copy variable output template statement": "Copy variable output template statement",
"Default headers is Content-Type: application/json": "Default headers is Content-Type: application/json",
"Ignore fail request and continue workflow": "Ignore fail request and continue workflow",
"Syntax see": "Syntax see",
"Show available variable tool": "Show available variable tool"
};

View File

@ -1,19 +1,14 @@
import { useTranslation } from 'react-i18next';
import { i18n } from '@nocobase/client';
import { useTranslation } from 'react-i18next';
import zhCN from './zh-CN';
import enUS from './en-US';
import jaJP from './ja-JP';
import ruRU from './ru-RU';
import trTR from './tr-TR';
export const NAMESPACE = 'workflow';
i18n.addResources('zh-CN', NAMESPACE, zhCN);
i18n.addResources('en-US', NAMESPACE, enUS);
i18n.addResources('ja-JP', NAMESPACE, jaJP);
i18n.addResources('ru-RU', NAMESPACE, ruRU);
i18n.addResources('tr-TR', NAMESPACE, trTR);
// i18n.addResources('zh-CN', NAMESPACE, zhCN);
// i18n.addResources('en-US', NAMESPACE, enUS);
// i18n.addResources('ja-JP', NAMESPACE, jaJP);
// i18n.addResources('ru-RU', NAMESPACE, ruRU);
// i18n.addResources('tr-TR', NAMESPACE, trTR);
export function lang(key: string, options = {}) {
return i18n.t(key, { ...options, ns: NAMESPACE });

View File

@ -3597,7 +3597,7 @@
dependencies:
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4":
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4":
version "7.16.3"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5"
integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==
@ -3646,6 +3646,13 @@
dependencies:
regenerator-runtime "^0.13.11"
"@babel/runtime@^7.20.6":
version "7.20.7"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd"
integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==
dependencies:
regenerator-runtime "^0.13.11"
"@babel/template@^7.10.4", "@babel/template@^7.16.7", "@babel/template@^7.4.0", "@babel/template@^7.4.4":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
@ -9980,6 +9987,13 @@ cross-env@^7.0.3:
dependencies:
cross-spawn "^7.0.1"
cross-fetch@3.1.5:
version "3.1.5"
resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
dependencies:
node-fetch "2.6.7"
cross-spawn-async@^2.1.1:
version "2.2.5"
resolved "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz#845ff0c0834a3ded9d160daca6d390906bb288cc"
@ -13487,12 +13501,19 @@ humanize-ms@^1.2.0, humanize-ms@^1.2.1:
dependencies:
ms "^2.0.0"
i18next@^21.6.0:
version "21.6.0"
resolved "https://registry.npmjs.org/i18next/-/i18next-21.6.0.tgz#257abf455b24136640a20728b44cf59f60cdeb5c"
integrity sha512-RjNuACL35wWZgtkyMcjcCmK7R72u3P6jTNbGKzrvHGI9M0iK5Vn1DsBIwOByppaXLIbe0viJ79Nz2h8w1UwPoQ==
i18next-http-backend@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.1.1.tgz#72a21d61c2e96eea9ad45ba1b9dd0090e119709a"
integrity sha512-jByfUCDVgQ8+/Wens7queQhYYvMcGTW/lR4IJJNEDDXnmqjLrwi8ubXKpmp76/JIWEZHffNdWqnxFJcTVGeaOw==
dependencies:
"@babel/runtime" "^7.12.0"
cross-fetch "3.1.5"
i18next@^22.4.9:
version "22.4.9"
resolved "https://registry.npmjs.org/i18next/-/i18next-22.4.9.tgz#98c8384c6bd41ff937da98b1e809ba03d3b41053"
integrity sha512-8gWMmUz460KJDQp/ob3MNUX84cVuDRY9PLFPnV8d+Qezz/6dkjxwOaH70xjrCNDO+JrUL25iXfAIN9wUkInNZw==
dependencies:
"@babel/runtime" "^7.20.6"
iconv-lite@0.4.24, iconv-lite@^0.4.15, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
version "0.4.24"
@ -17441,6 +17462,13 @@ node-fetch@2.6.0:
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
node-fetch@2.6.7, node-fetch@^2.6.7:
version "2.6.7"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
node-fetch@^2.6.1:
version "2.6.6"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
@ -17448,13 +17476,6 @@ node-fetch@^2.6.1:
dependencies:
whatwg-url "^5.0.0"
node-fetch@^2.6.7:
version "2.6.7"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
node-gyp@8.x:
version "8.4.1"
resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937"