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 my-nocobase-app/packages/app/client/src/.umi \
&& rm -rf nocobase.tar.gz \ && rm -rf nocobase.tar.gz \
&& rm -rf ./my-nocobase-app/node_modules/@antv \ && 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/@ant-design \
&& rm -rf ./my-nocobase-app/node_modules/china-division/dist/villages.json \ && 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 '*.csv' -delete \

View File

@ -30,9 +30,11 @@ RUN cd /app \
RUN cd /app \ RUN cd /app \
&& rm -rf my-nocobase-app/packages/app/client/src/.umi \ && rm -rf my-nocobase-app/packages/app/client/src/.umi \
&& rm -rf nocobase.tar.gz \ && rm -rf nocobase.tar.gz \
&& rm -rf ./my-nocobase-app/nocobase/node_modules/@antv \ && rm -rf ./my-nocobase-app/node_modules/@antv \
&& rm -rf ./my-nocobase-app/nocobase/node_modules/antd \ && rm -rf ./my-nocobase-app/node_modules/antd/dist \
&& rm -rf ./my-nocobase-app/nocobase/node_modules/@ant-design \ && 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 \ && 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 '*.csv' -delete \
&& find ./my-nocobase-app/node_modules/china-division/dist -name '*.sqlite' -delete \ && find ./my-nocobase-app/node_modules/china-division/dist -name '*.sqlite' -delete \

View File

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

View File

@ -1,14 +1,21 @@
import { ConfigProvider, Spin } from 'antd'; 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 { useTranslation } from 'react-i18next';
import { useAPIClient, useRequest } from '../api-client'; 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) { export function AntdConfigProvider(props) {
const { remoteLocale, ...others } = props; const { remoteLocale, ...others } = props;
const api = useAPIClient(); const api = useAPIClient();
const { i18n } = useTranslation(); const { i18n } = useTranslation();
const { loading } = useRequest( const { data, loading } = useRequest(
{ {
url: 'app:getLang', url: 'app:getLang',
}, },
@ -19,6 +26,12 @@ export function AntdConfigProvider(props) {
api.auth.setLocale(data?.data?.lang); api.auth.setLocale(data?.data?.lang);
i18n.changeLanguage(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, manual: !remoteLocale,
}, },
@ -27,12 +40,10 @@ export function AntdConfigProvider(props) {
return <Spin />; return <Spin />;
} }
return ( return (
<ConfigProvider <AppLangContext.Provider value={data?.data}>
dropdownMatchSelectWidth={false} <ConfigProvider dropdownMatchSelectWidth={false} {...others} locale={data?.data?.antd || {}}>
{...others} {props.children}
locale={locale[i18n.language].antd} </ConfigProvider>
> </AppLangContext.Provider>
{props.children}
</ConfigProvider>
); );
} }

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

View File

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

View File

@ -43,6 +43,7 @@ export default {
"Super admin": "Super admin", "Super admin": "Super admin",
"Language": "Language", "Language": "Language",
"Allow sign up": "Allow sign up", "Allow sign up": "Allow sign up",
"Enable SMS authentication": "Enable SMS authentication",
"Sign out": "Sign out", "Sign out": "Sign out",
"Cancel": "Cancel", "Cancel": "Cancel",
"Submit": "Submit", "Submit": "Submit",
@ -53,12 +54,12 @@ export default {
"Form": "Form", "Form": "Form",
"Select data source": "Select data source", "Select data source": "Select data source",
"Calendar": "Calendar", "Calendar": "Calendar",
'Delete events': 'Delete events', "Delete events": "Delete events",
'This event': 'This event', "This event": "This event",
'This and following events': 'This and following events', "This and following events": "This and following events",
'All events': 'All events', "All events": "All events",
'Delete this event?': 'Delete this event?', "Delete this event?": "Delete this event?",
'Delete Event': 'Delete Event', "Delete Event": "Delete Event",
"Kanban": "Kanban", "Kanban": "Kanban",
"Select grouping field": "Select grouping field", "Select grouping field": "Select grouping field",
"Media": "Media", "Media": "Media",
@ -114,11 +115,12 @@ export default {
"Collection display name": "Collection display name", "Collection display name": "Collection display name",
"Collection name": "Collection name", "Collection name": "Collection name",
"Inherits": "Inherits", "Inherits": "Inherits",
"AutoGenId": "Auto-generated ID field", "Generate ID field automatically": "Generate ID field automatically",
"CreatedBy": "Recording a row's created user", "Store the creation user of each record": "Store the creation user of each record",
"UpdatedBy": "Recording a row's last updated user", "Store the last update user of each record": "Store the last update user of each record",
"CreatedAt": "Recording a row's created time ", "Store the creation time of each record": "Store the creation time of each record",
"UpdatedAt": "Recording a row's last updated user", "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", "Records can be sorted": "Records can be sorted",
"Collection template": "Collection template", "Collection template": "Collection template",
"Calendar collection": "Calendar collection", "Calendar collection": "Calendar collection",
@ -136,9 +138,9 @@ export default {
"Association fields filter": "Association fields filter", "Association fields filter": "Association fields filter",
"PK & FK fields": "PK & FK fields", "PK & FK fields": "PK & FK fields",
"Association fields": "Association fields", "Association fields": "Association fields",
"Parent collection fields": "Parent collection fields",
"System fields": "System fields", "System fields": "System fields",
"General fields": "General fields", "General fields": "General fields",
"Parent collection fields": "Parent collection fields",
"Basic": "Basic", "Basic": "Basic",
"Single line text": "Single line text", "Single line text": "Single line text",
"Long text": "Long text", "Long text": "Long text",
@ -183,6 +185,7 @@ export default {
"12 hour": "12 hour", "12 hour": "12 hour",
"24 hour": "24 hour", "24 hour": "24 hour",
"Relationship type": "Relationship type", "Relationship type": "Relationship type",
"Inverse relationship type": "Inverse relationship type",
"Source collection": "Source collection", "Source collection": "Source collection",
"Source key": "Source key", "Source key": "Source key",
"Target collection": "Target collection", "Target collection": "Target collection",
@ -193,12 +196,28 @@ export default {
"One to many": "One to many", "One to many": "One to many",
"Many to one": "Many to one", "Many to one": "Many to one",
"Many to many": "Many to many", "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 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.", "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 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.", "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", "Generated automatically if left blank": "Generated automatically if left blank",
"Foreign key 2": "Foreign key 2", "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": "Add filter",
"Add filter group": "Add filter group", "Add filter group": "Add filter group",
"Comparision": "Comparision", "Comparision": "Comparision",
@ -219,6 +238,7 @@ export default {
"Edit button": "Edit button", "Edit button": "Edit button",
"Hide": "Hide", "Hide": "Hide",
"Enable actions": "Enable actions", "Enable actions": "Enable actions",
"Import": "Import",
"Export": "Export", "Export": "Export",
"Customize": "Customize", "Customize": "Customize",
"Function": "Function", "Function": "Function",
@ -239,9 +259,30 @@ export default {
"Custom column name": "Custom column name", "Custom column name": "Custom column name",
"Edit description": "Edit description", "Edit description": "Edit description",
"Required": "Required", "Required": "Required",
"Unique": "Unique",
"Label field": "Label field", "Label field": "Label field",
"Default is the ID field": "Default is the ID field", "Default is the ID field": "Default is the ID field",
"Set default sorting rules": "Set default sorting rules", "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 before": "is before",
"is after": "is after", "is after": "is after",
"is on or after": "is on or after", "is on or after": "is on or after",
@ -275,6 +316,7 @@ export default {
"Configure calendar": "Configure calendar", "Configure calendar": "Configure calendar",
"Title field": "Title field", "Title field": "Title field",
"Custom title": "Custom title", "Custom title": "Custom title",
"Show lunar": "Show lunar",
"Start date field": "Start date field", "Start date field": "Start date field",
"End date field": "End date field", "End date field": "End date field",
"Navigate": "Navigate", "Navigate": "Navigate",
@ -282,10 +324,13 @@ export default {
"Description": "Description", "Description": "Description",
"Select view": "Select view", "Select view": "Select view",
"Reset": "Reset", "Reset": "Reset",
"Importable fields": "Importable fields",
"Exportable fields": "Exportable fields", "Exportable fields": "Exportable fields",
"Saved successfully": "Saved successfully", "Saved successfully": "Saved successfully",
"Nickname": "Nickname", "Nickname": "Nickname",
"Sign in": "Sign in", "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", "Create an account": "Create an account",
"Sign up": "Sign up", "Sign up": "Sign up",
"Confirm password": "Confirm password", "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.", "Signed up successfully. It will jump to the login page.": "Signed up successfully. It will jump to the login page.",
"Password mismatch": "Password mismatch", "Password mismatch": "Password mismatch",
"Users": "Users", "Users": "Users",
"Verification code": "Verification code",
"Send code": "Send code",
"Retry after {{count}} seconds": "Retry after {{count}} seconds",
"Roles": "Roles", "Roles": "Roles",
"Add role": "Add role", "Add role": "Add role",
"Role name": "Role name", "Role name": "Role name",
@ -355,6 +403,7 @@ export default {
"DESC": "DESC", "DESC": "DESC",
"Add sort field": "Add sort field", "Add sort field": "Add sort field",
"ID": "ID", "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", "Drawer": "Drawer",
"Dialog": "Dialog", "Dialog": "Dialog",
"Delete action": "Delete action", "Delete action": "Delete action",
@ -377,6 +426,7 @@ export default {
"exists": "exists", "exists": "exists",
"not exists": "not exists", "not exists": "not exists",
"is current logged-in user": "is current logged-in user", "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 calendar block": "Create calendar block",
"Create kanban block": "Create kanban block", "Create kanban block": "Create kanban block",
"Grouping field": "Grouping field", "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", "Tab name": "Tab name",
"Current record blocks": "Current record blocks", "Current record blocks": "Current record blocks",
"Popup message": "Popup message", "Popup message": "Popup message",
@ -419,10 +470,13 @@ export default {
"General permissions": "General permissions", "General permissions": "General permissions",
"Global action permissions": "Global action permissions", "Global action permissions": "Global action permissions",
"General action permissions": "General action permissions", "General action permissions": "General action permissions",
"Plugin settings permissions":"Plugin settings permissions", "Plugin settings permissions": "Plugin settings permissions",
"Allow to desgin pages":"Allow to desgin pages", "Allow to desgin pages": "Allow to desgin pages",
"Allow to manage plugins":"Allow to manage plugins", "Allow to manage plugins": "Allow to manage plugins",
"Allow to configure plugins":"Allow to configure 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", "Action display name": "Action display name",
"Allow": "Allow", "Allow": "Allow",
"Data scope": "Data scope", "Data scope": "Data scope",
@ -449,8 +503,11 @@ export default {
"Use the built-in static file server": "Use the built-in static file server", "Use the built-in static file server": "Use the built-in static file server",
"Local storage": "Local storage", "Local storage": "Local storage",
"Aliyun OSS": "Aliyun OSS", "Aliyun OSS": "Aliyun OSS",
"Tencent COS": "Tencent COS",
"Amazon S3": "Amazon S3", "Amazon S3": "Amazon S3",
"Tencent COS": "Tencent COS",
"Region": "Region",
"Bucket": "Bucket",
"Path": "Path",
"Unsaved changes": "Unsaved changes", "Unsaved changes": "Unsaved changes",
"Are you sure you don't want to save?": "Are you sure you don't want to save?", "Are you sure you don't want to save?": "Are you sure you don't want to save?",
"Dragging": "Dragging", "Dragging": "Dragging",
@ -462,6 +519,7 @@ export default {
"Dynamic value": "Dynamic value", "Dynamic value": "Dynamic value",
"Current user": "Current user", "Current user": "Current user",
"Current record": "Current record", "Current record": "Current record",
"Current time": "Current time",
"Popup close method": "Popup close method", "Popup close method": "Popup close method",
"Automatic close": "Automatic close", "Automatic close": "Automatic close",
"Manually close": "Manually close", "Manually close": "Manually close",
@ -489,29 +547,40 @@ export default {
"Record ID": "Record ID", "Record ID": "Record ID",
"User": "User", "User": "User",
"Field": "Field", "Field": "Field",
"Select": "Select",
"Select Field": "Select Field",
"Field value changes": "Field value changes", "Field value changes": "Field value changes",
"One to one (has one)": "One to one (has one)", "One to one (has one)": "One to one (has one)",
"One to one (belongs to)": "One to one (belongs to)", "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", "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", "Province/city/area name": "Province/city/area name",
"Field component": "Field component",
"Subtable": "Subtable",
"Subform": "Subform",
"Regular expression": "Pattern",
"Enabled languages": "Enabled languages", "Enabled languages": "Enabled languages",
"View all plugins": "View all plugins", "View all plugins": "View all plugins",
"Print": "Print", "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', "Done": "Done",
'Sign up successfully, and automatically jump to the sign in page': 'Sign up successfully, and automatically jump to the sign in page', "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", "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", "Bulk edit": "Bulk edit",
"Data will be updated": "Data will be updated", "Data will be updated": "Data will be updated",
"Selected": "Selected", "Selected": "Selected",
"All": "All",
"Update selected data?": "Update selected data?",
"Update all data?": "Update all data?",
"Remains the same": "Remains the same", "Remains the same": "Remains the same",
"Changed to": "Changed to", "Changed to": "Changed to",
"Clear": "Clear", "Clear": "Clear",
@ -520,6 +589,13 @@ export default {
"Selector": "Selector", "Selector": "Selector",
"Inner": "Inner", "Inner": "Inner",
"Search and select collection": "Search and select collection", "Search and select collection": "Search and select collection",
'Please fill in the iframe URL': 'Please fill in the iframe URL', "Please fill in the iframe URL": "Please fill in the iframe URL",
'Fix block': 'Fix block' "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 = { export type LocaleOptions = {
label: string; label: string;
moment: string;
antd: any;
resources?: any;
}; };
export { default as cron } from '../schema-component/antd/cron/locale';
export default { export default {
'en-US': { 'ar-EG': { label: 'العربية' },
label: 'English', 'az-AZ': { label: 'Azərbaycan dili' },
// https://github.com/moment/moment/blob/develop/locale/en.js 'bg-BG': { label: 'Български' },
moment: 'en', 'bn-BD': { label: 'Bengali' },
// https://github.com/ant-design/ant-design/tree/master/components/locale/en_US 'by-BY': { label: 'Беларускі' },
antd: antdEnUS, 'ca-ES': { label: 'Сatalà/Espanya' },
resources: { 'cs-CZ': { label: 'Česky' },
client: { 'da-DK': { label: 'Dansk' },
...enUS, 'de-DE': { label: 'Deutsch' },
}, 'el-GR': { label: 'Ελληνικά' },
}, 'en-GB': { label: 'English(GB)' },
}, 'en-US': { label: 'English' },
'ja-JP': { 'es-ES': { label: 'Español' },
label: '日本語', 'et-EE': { label: 'Estonian (Eesti)' },
// https://github.com/moment/moment/blob/develop/locale/ja.js 'fa-IR': { label: 'فارسی' },
moment: 'ja', 'fi-FI': { label: 'Suomi' },
// https://github.com/ant-design/ant-design/tree/master/components/locale/ja_JP 'fr-BE': { label: 'Français(BE)' },
antd: antdJaJP, 'fr-CA': { label: 'Français(CA)' },
resources: { 'fr-FR': { label: 'Français' },
client: { 'ga-IE': { label: 'Gaeilge' },
...jaJP, 'gl-ES': { label: 'Galego' },
}, 'he-IL': { label: 'עברית' },
}, 'hi-IN': { label: 'हिन्दी' },
}, 'hr-HR': { label: 'Hrvatski jezik' },
'zh-CN': { 'hu-HU': { label: 'Magyar' },
label: '简体中文', 'hy-AM': { label: 'Հայերեն' },
// https://github.com/moment/moment/blob/develop/locale/zh-cn.js 'id-ID': { label: 'Bahasa Indonesia' },
moment: 'zh-cn', 'is-IS': { label: 'Íslenska' },
// https://github.com/ant-design/ant-design/tree/master/components/locale/zh_CN 'it-IT': { label: 'Italiano' },
antd: antdZhCN, 'ja-JP': { label: '日本語' },
// i18next 'ka-GE': { label: 'ქართული' },
resources: { 'kk-KZ': { label: 'Қазақ тілі' },
client: { 'km-KH': { label: 'ភាសាខ្មែរ' },
...zhCN, // 'kmr-IQ': { label: 'kmr_IQ' },
}, 'kn-IN': { label: 'ಕನ್ನಡ' },
}, 'ko-KR': { label: '한국어' },
}, 'ku-IQ': { label: 'کوردی' },
'ru-RU': { 'lt-LT': { label: 'lietuvių' },
label: 'Русский', 'lv-LV': { label: 'Latviešu valoda' },
// https://github.com/moment/moment/blob/develop/locale/ru.js 'mk-MK': { label: 'македонски јазик' },
moment: 'ru', 'ml-IN': { label: 'മലയാളം' },
// https://github.com/ant-design/ant-design/tree/master/components/locale/ru_RU 'mn-MN': { label: 'Монгол хэл' },
antd: antdRuRU, 'ms-MY': { label: 'بهاس ملايو' },
resources: { 'nb-NO': { label: 'Norsk bokmål' },
client: { 'ne-NP': { label: 'नेपाली' },
...ruRU, 'nl-BE': { label: 'Vlaams' },
}, 'nl-NL': { label: 'Nederlands' },
}, 'pl-PL': { label: 'Polski' },
}, 'pt-BR': { label: 'Português brasileiro' },
'tr-TR': { 'pt-PT': { label: 'Português' },
label: 'Türkçe', 'ro-RO': { label: 'România' },
// https://github.com/moment/moment/blob/develop/locale/tr.js 'ru-RU': { label: 'Русский' },
moment: 'tr', 'si-LK': { label: 'සිංහල' },
// https://github.com/ant-design/ant-design/tree/master/components/locale/tr_TR 'sk-SK': { label: 'Slovenčina' },
antd: antdTrTR, 'sl-SI': { label: 'Slovenščina' },
resources: { 'sr-RS': { label: 'српски језик' },
client: { 'sv-SE': { label: 'Svenska' },
...trTR, 'ta-IN': { label: 'Tamil' },
}, 'th-TH': { label: 'ภาษาไทย' },
}, 'tk-TK': { label: 'Turkmen' },
}, 'tr-TR': { label: 'Türkçe' },
} as Record<string, LocaleOptions>; '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 { 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 = { const Input = (props: Exclude<CronProps, 'setValue'> & { onChange: (value: string) => void }) => {
'zh-CN': localeZhCN, const { onChange, ...rest } = props;
}; return (
<fieldset
const ReadPrettyLocales = { className={css`
'en-US': 'en', .react-js-cron {
'zh-CN': 'zh_CN' padding: 0.5em 0.5em 0 0.5em;
};
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;
border: 1px dashed #ccc; border: 1px dashed #ccc;
.react-js-cron-field {
.react-js-cron-field{
flex-shrink: 0; flex-shrink: 0;
margin-bottom: .5em; margin-bottom: 0.5em;
> span{ > span {
flex-shrink: 0; flex-shrink: 0;
margin: 0 .5em 0 0; margin: 0 0.5em 0 0;
} }
> .react-js-cron-select{ > .react-js-cron-select {
margin: 0 .5em 0 0; margin: 0 0.5em 0 0;
} }
} }
`}> }
<ReactCron `}
setValue={onChange} >
locale={locale} <ReactCron setValue={onChange} locale={window['cronLocale']} {...rest} />
{...rest} </fieldset>
/> );
</fieldset> };
);
}, const ReadPretty = (props) => {
mapReadPretty((props) => { const api = useAPIClient();
const locale = ReadPrettyLocales[localStorage.getItem('NOCOBASE_LOCALE') || 'en-US']; const locale = api.auth.getLocale();
return props.value return props.value ? (
? ( <span>
<span> {cronstrue.toString(props.value, {
{cronstrue.toString(props.value, { locale,
locale, use24HourTimeFormat: true,
use24HourTimeFormat: true })}
})} </span>
</span> ) : null;
) };
: null;
}) export const Cron: ComposedCron = connect(Input, mapReadPretty(ReadPretty));
);
export default Cron; 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) => { const langs = Object.keys(locale).map((lang) => {
return { return {
label: locale[lang].label, label: `${locale[lang].label} (${lang})`,
value: lang, value: lang,
}; };
}); });

View File

@ -16,7 +16,7 @@
"chalk": "^4.1.1", "chalk": "^4.1.1",
"commander": "^9.2.0", "commander": "^9.2.0",
"find-package-json": "^1.2.0", "find-package-json": "^1.2.0",
"i18next": "^21.6.0", "i18next": "^22.4.9",
"koa": "^2.13.4", "koa": "^2.13.4",
"koa-bodyparser": "^4.3.0", "koa-bodyparser": "^4.3.0",
"koa-static": "^5.0.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 }); const i18n = ctx.app.i18n.cloneInstance({ initImmediate: false });
ctx.i18n = i18n; ctx.i18n = i18n;
ctx.t = i18n.t.bind(i18n); ctx.t = i18n.t.bind(i18n);
const lng = ctx.getCurrentLocale = () => {
ctx.get('X-Locale') || const lng =
(ctx.request.query.locale as string) || ctx.get('X-Locale') ||
ctx.app.i18n.language || (ctx.request.query.locale as string) ||
ctx.acceptsLanguages().shift() || ctx.app.i18n.language ||
'en-US'; ctx.acceptsLanguages().shift() ||
'en-US';
return lng;
};
const lng = ctx.getCurrentLocale();
if (lng !== '*' && lng) { if (lng !== '*' && lng) {
i18n.changeLanguage(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 { Plugin, PluginManager } from '@nocobase/server';
import send from 'koa-send'; import send from 'koa-send';
import serve from 'koa-static'; import serve from 'koa-static';
import isEmpty from 'lodash/isEmpty';
import { isAbsolute, resolve } from 'path'; 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 { export class ClientPlugin extends Plugin {
async beforeLoad() { async beforeLoad() {
@ -23,6 +29,7 @@ export class ClientPlugin extends Plugin {
this.app.acl.allow('app', 'getPlugins'); this.app.acl.allow('app', 'getPlugins');
this.app.acl.allow('plugins', 'getPinned', 'loggedIn'); this.app.acl.allow('plugins', 'getPinned', 'loggedIn');
const dialect = this.app.db.sequelize.getDialect(); const dialect = this.app.db.sequelize.getDialect();
const locales = require('./locale').default;
this.app.resource({ this.app.resource({
name: 'app', name: 'app',
actions: { actions: {
@ -53,8 +60,28 @@ export class ClientPlugin extends Plugin {
if (enabledLanguages.includes(currentUser?.appLang)) { if (enabledLanguages.includes(currentUser?.appLang)) {
lang = 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 = { ctx.body = {
lang, lang,
moment: getMomentLocale(lang),
...locales[lang],
}; };
await next(); await next();
}, },

View File

@ -1,4 +1,6 @@
export default { export default {
'unique violation': '{{field}} must be unique', "unique violation": "{{field}} must be unique",
'notNull Violation': '{{field}} cannot be null', "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 React, { useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { GraphCollectionPane, GraphCollectionShortcut } from './GraphCollectionShortcut'; import { GraphCollectionPane, GraphCollectionShortcut } from './GraphCollectionShortcut';
import { enUS, jaJP, zhCN } from './locale'; import { useGCMTranslation } from './utils';
export const GraphCollectionProvider = React.memo((props) => { export const GraphCollectionProvider = React.memo((props) => {
const ctx = useContext(PluginManagerContext); const ctx = useContext(PluginManagerContext);
const { routes, components, ...others } = useContext(RouteSwitchContext); const { routes, components, ...others } = useContext(RouteSwitchContext);
i18n.addResources('en-US', 'graphPositions', enUS); // i18n.addResources('en-US', 'graphPositions', enUS);
i18n.addResources('ja-JP', 'graphPositions', jaJP); // i18n.addResources('ja-JP', 'graphPositions', jaJP);
i18n.addResources('zh-CN', 'graphPositions', zhCN); // i18n.addResources('zh-CN', 'graphPositions', zhCN);
const { t } = useTranslation('graphPositions'); const { t } = useGCMTranslation();
const items = useContext(SettingsCenterContext); const items = useContext(SettingsCenterContext);
items['collection-manager']['tabs']['graph'] = { items['collection-manager']['tabs']['graph'] = {

View File

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

View File

@ -1,10 +1,8 @@
import { import {
ApartmentOutlined, ApartmentOutlined,
FullscreenExitOutlined, FullscreenExitOutlined,
FullscreenOutlined, FullscreenOutlined, LineHeightOutlined, MenuOutlined,
MenuOutlined, ShareAltOutlined
ShareAltOutlined,
LineHeightOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { Graph } from '@antv/x6'; import { Graph } from '@antv/x6';
import '@antv/x6-react-shape'; import '@antv/x6-react-shape';
@ -12,30 +10,25 @@ import { css, cx } from '@emotion/css';
import { SchemaOptionsContext } from '@formily/react'; import { SchemaOptionsContext } from '@formily/react';
import { import {
APIClientProvider, APIClientProvider,
collection, collection, CollectionManagerContext, CollectionManagerProvider,
CollectionManagerProvider,
CurrentAppInfoContext, CurrentAppInfoContext,
SchemaComponent, SchemaComponent,
SchemaComponentOptions, SchemaComponentOptions, Select, useAPIClient,
useAPIClient,
useCollectionManager, useCollectionManager,
useCompile, useCompile,
useCurrentAppInfo, useCurrentAppInfo
Select,
CollectionManagerContext,
} from '@nocobase/client'; } from '@nocobase/client';
import { useFullscreen } from 'ahooks'; 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 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 React, { createContext, forwardRef, useContext, useEffect, useLayoutEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useAsyncDataSource, useCreateActionAndRefreshCM } from './action-hooks'; import { useAsyncDataSource, useCreateActionAndRefreshCM } from './action-hooks';
import { AddCollectionAction } from './components/AddCollectionAction'; import { AddCollectionAction } from './components/AddCollectionAction';
import Entity from './components/Entity'; import Entity from './components/Entity';
import { collectionListClass, graphCollectionContainerClass } from './style';
import { formatData, getDiffEdge, getDiffNode, getInheritCollections, getChildrenCollections } from './utils';
import { SimpleNodeView } from './components/ViewNode'; import { SimpleNodeView } from './components/ViewNode';
import { collectionListClass, graphCollectionContainerClass } from './style';
import { formatData, getChildrenCollections, getDiffEdge, getDiffNode, getInheritCollections, useGCMTranslation } from './utils';
const LINE_HEIGHT = 40; const LINE_HEIGHT = 40;
const NODE_WIDTH = 250; const NODE_WIDTH = 250;
@ -360,7 +353,7 @@ export const GraphDrawPage = React.memo(() => {
const ctx = useContext(CollectionManagerContext); const ctx = useContext(CollectionManagerContext);
const api = useAPIClient(); const api = useAPIClient();
const compile = useCompile(); const compile = useCompile();
const { t } = useTranslation('graphPositions'); const { t } = useGCMTranslation();
const [collectionData, setCollectionData] = useState<any>([]); const [collectionData, setCollectionData] = useState<any>([]);
const [collectionList, setCollectionList] = useState<any>([]); const [collectionList, setCollectionList] = useState<any>([]);
const { refreshCM } = useCollectionManager(); 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 '@antv/x6-react-shape';
import { css, cx } from '@emotion/css'; import { css, cx } from '@emotion/css';
import { uid } from '@formily/shared'; import { uid } from '@formily/shared';
import { import {
Action, Action,
Checkbox, Checkbox,
collection,
CollectionField, CollectionField,
CollectionProvider, CollectionProvider,
Form, Form,
FormItem, FormItem,
Formula,
Grid, Grid,
Input, Input,
InputNumber, InputNumber,
@ -19,31 +21,29 @@ import {
Select, Select,
useCollectionManager, useCollectionManager,
useCompile, useCompile,
Formula,
useRecord,
collection,
useCurrentAppInfo, useCurrentAppInfo,
useRecord
} from '@nocobase/client'; } from '@nocobase/client';
import { useTranslation } from 'react-i18next'; import { Badge, Dropdown, Popover, Tag } from 'antd';
import { Dropdown, Popover, Tag, Badge } from 'antd';
import React, { useRef, useState } from 'react';
import { groupBy } from 'lodash'; import { groupBy } from 'lodash';
import React, { useRef, useState } from 'react';
import { import {
useAsyncDataSource, useAsyncDataSource,
useCancelAction, useCancelAction,
useDestroyActionAndRefreshCM, useDestroyActionAndRefreshCM,
useDestroyFieldActionAndRefreshCM, useDestroyFieldActionAndRefreshCM,
useUpdateCollectionActionAndRefreshCM, useUpdateCollectionActionAndRefreshCM,
useValuesFromRecord, useValuesFromRecord
} from '../action-hooks'; } from '../action-hooks';
import { collectiionPopoverClass, entityContainer, headClass, tableBtnClass, tableNameClass } from '../style'; import { collectiionPopoverClass, entityContainer, headClass, tableBtnClass, tableNameClass } from '../style';
import { FieldSummary } from './FieldSummary'; import { useGCMTranslation } from '../utils';
import { AddFieldAction } from './AddFieldAction'; import { AddFieldAction } from './AddFieldAction';
import { CollectionNodeProvder } from './CollectionNodeProvder'; import { CollectionNodeProvder } from './CollectionNodeProvder';
import { EditCollectionAction } from './EditCollectionAction';
import { EditFieldAction } from './EditFieldAction'; import { EditFieldAction } from './EditFieldAction';
import { FieldSummary } from './FieldSummary';
import { OverrideFieldAction } from './OverrideFieldAction'; import { OverrideFieldAction } from './OverrideFieldAction';
import { ViewFieldAction } from './ViewFieldAction'; import { ViewFieldAction } from './ViewFieldAction';
import { EditCollectionAction } from './EditCollectionAction';
const Entity: React.FC<{ const Entity: React.FC<{
node?: Node | any; node?: Node | any;
@ -166,7 +166,7 @@ const PortsCom = React.memo<any>(({ targetGraph, collectionData, setTargetNode,
}, },
} = node; } = node;
const [collapse, setCollapse] = useState(false); const [collapse, setCollapse] = useState(false);
const { t } = useTranslation('graphPositions'); const { t } = useGCMTranslation();
const compile = useCompile(); const compile = useCompile();
const portsData = groupBy(ports.items, (v) => { const portsData = groupBy(ports.items, (v) => {
if ( 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 = { const shape = {
ER: 'er-rect', ER: 'er-rect',
EDGE: 'edge', EDGE: 'edge',
}; };
export const useGCMTranslation = () => {
return useTranslation('graph-collection-manager');
};
export const getInheritCollections = (collections, name) => { export const getInheritCollections = (collections, name) => {
const parents = []; const parents = [];
const getParents = (name) => { 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('zh-CN', NAMESPACE, zhCN);
i18n.addResources('en-US', NAMESPACE, enUS); // i18n.addResources('en-US', NAMESPACE, enUS);
export * from './ImportActionInitializer'; export * from './ImportActionInitializer';
export * from './ImportDesigner'; export * from './ImportDesigner';
@ -11,3 +8,4 @@ export * from './ImportInitializerProvider';
export * from './ImportPluginProvider'; export * from './ImportPluginProvider';
export { ImportPluginProvider as default } from './ImportPluginProvider'; export { ImportPluginProvider as default } from './ImportPluginProvider';
export * from './useImportAction'; export * from './useImportAction';

View File

@ -1,23 +1,27 @@
export default { export default {
'Only one file is allowed to be uploaded': 'Only one file is allowed to be uploaded', "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', "File size cannot exceed 10M": "File size cannot exceed 10M",
'Please upload the file of Excel': 'Please upload the file of Excel', "Please upload the file of Excel": "Please upload the file of Excel",
'Import Data': 'Import Data', "Import Data": "Import Data",
'Start import': 'Start import', "Start import": "Start import",
'Import explain': 'Guide', "Import explain": "Guide",
'Download template': 'Download template', "Download template": "Download template",
'Step 1: Download template': 'Step 1: Download template', "Step 1: Download template": "Step 1: Download template",
'Step 2: Upload Excel': 'Step 2: Upload Excel', "Step 2: Upload Excel": "Step 2: Upload Excel",
'Download tip': "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",
'- 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",
'Upload placeholder': 'Drag and drop the file here or click to upload, file size should not exceed 10M', "Excel data importing": "Excel data importing",
'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",
'Import done, total success have {{successCount}} , total failure have {{failureCount}}': "To download the failure data": "To download the failure data",
'Import is complete, with a total of {{successCount}} successful and {{failureCount}} failed', "Add importable field": "Add importable field",
'To download the failure data': 'To download the failure data', "Done": "Done",
'Add importable field': 'Add importable field', "Yes": "Yes",
Done: 'Done', "No": "No",
Yes: 'Yes', "Field {{fieldName}} does not exist": "Field {{fieldName}} does not exist",
No: 'No', "can not find value": "can not find value",
'Field {{fieldName}} does not exist': 'Field {{fieldName}} does not exist', "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 AMapLoader from '@amap/amap-jsapi-loader';
import '@amap/amap-jsapi-types'; import '@amap/amap-jsapi-types';
import { SyncOutlined } from '@ant-design/icons';
import { css } from '@emotion/css';
import { useFieldSchema } from '@formily/react'; import { useFieldSchema } from '@formily/react';
import { useCollection } from '@nocobase/client'; 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 { 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 { useHistory } from 'react-router';
import { SyncOutlined } from '@ant-design/icons'; import { useMapConfiguration } from '../hooks';
import { useMapTranslation } from '../locale';
import Search from './Search';
interface AMapComponentProps { interface AMapComponentProps {
accessKey: string; 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 { useBoolean } from 'ahooks';
import { Form, Input, Tabs, Button, Card, message } from 'antd'; import { Button, Card, Form, Input, message, Tabs } from 'antd';
import React, { useMemo, useEffect } from 'react'; import React, { useEffect, useMemo } from 'react';
import { MapTypes } from '../constants'; import { MapTypes } from '../constants';
import { MapConfigurationResourceKey, useMapConfiguration } from '../hooks'; import { MapConfigurationResourceKey, useMapConfiguration } from '../hooks';
import { useMapTranslation } from '../locales'; import { useMapTranslation } from '../locale';
const AMapConfiguration = ({ type }) => { const AMapConfiguration = ({ type }) => {
const { t } = useMapTranslation(); const { t } = useMapTranslation();

View File

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

View File

@ -1,8 +1,8 @@
import { message, Select } from 'antd';
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import React, { useEffect, useRef, useState } from 'react';
import { useDebounceFn } from 'ahooks'; 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 { interface SearchProps {
aMap: any; aMap: any;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,8 @@
export default { export default {
'Please fill in your email address': 'Please fill in your email address', "The email is incorrect, please re-enter": "The email is incorrect, please re-enter",
'The password is incorrect, please re-enter': 'The password 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 { i18n } from '@nocobase/client';
import { useTranslation } from 'react-i18next';
import zhCN from './zh-CN';
export const NAMESPACE = 'verification'; export const NAMESPACE = 'verification';
i18n.addResources('zh-CN', NAMESPACE, zhCN); // i18n.addResources('zh-CN', NAMESPACE, zhCN);
export function lang(key: string) { export function lang(key: string) {
return i18n.t(key, { ns: NAMESPACE }); return i18n.t(key, { ns: NAMESPACE });

View File

@ -1,14 +1,21 @@
export default { export default {
"Workflow": "Workflow", "Workflow": "Workflow",
"Execution history": "Execution history", "Execution history": "Execution history",
"Executed": "Executed",
"Trigger type": "Trigger type", "Trigger type": "Trigger type",
"Status": "Status", "Status": "Status",
"On": "On", "On": "On",
"Off": "Off", "Off": "Off",
"Version": "Version", "Version": "Version",
"Copy to new version": "Copy to new version", "Copy to new version": "Copy to new version",
"Duplicate": "Duplicate",
"Loading": "Loading",
"Load failed": "Load failed", "Load failed": "Load failed",
"Trigger": "Trigger", "Trigger": "Trigger",
"Trigger variables": "Trigger variables",
"Trigger data": "Trigger data",
"Trigger time": "Trigger time",
"Triggered at": "Triggered at",
"Collection event": "Collection event", "Collection event": "Collection event",
"Trigger on": "Trigger on", "Trigger on": "Trigger on",
"After record added": "After record added", "After record added": "After record added",
@ -24,6 +31,7 @@ export default {
"Based on date field of collection": "Based on date field of collection", "Based on date field of collection": "Based on date field of collection",
"Starts on": "Starts on", "Starts on": "Starts on",
"Ends on": "Ends on", "Ends on": "Ends on",
"No end": "No end",
"Exactly at": "Exactly at", "Exactly at": "Exactly at",
"Repeat mode": "Repeat mode", "Repeat mode": "Repeat mode",
"Repeat limit": "Repeat limit", "Repeat limit": "Repeat limit",
@ -45,36 +53,51 @@ export default {
"By custom date": "By custom date", "By custom date": "By custom date",
"Advanced": "Advanced", "Advanced": "Advanced",
"End": "End", "End": "End",
"Trigger variables": "Trigger variables",
"Node result": "Node result", "Node result": "Node result",
"Constant": "Constant", "Constant": "Constant",
"Null": "Null",
"Boolean": "Boolean", "Boolean": "Boolean",
"String": "String", "String": "String",
"Calculator": "Calculator",
"Arithmetic calculation": "Arithmetic calculation", "Arithmetic calculation": "Arithmetic calculation",
"String operation": "String operation", "String operation": "String operation",
"Executed at": "Executed at",
"Queueing": "Queueing",
"On going": "On going", "On going": "On going",
"Succeeded": "Succeeded", "Succeeded": "Succeeded",
"Failed": "Failed", "Failed": "Failed",
"Pending": "Pending",
"Canceled": "Canceled", "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?", "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", "Control": "Control",
"Collection operations": "Collection operations", "Collection operations": "Collection operations",
"Extended types": "Extended types",
"Node type": "Node type", "Node type": "Node type",
"Calculation": "Calculation", "Calculation": "Calculation",
"Configure calculation": "Configure calculation", "Configure calculation": "Configure calculation",
"Calculation result": "Calculation result", "Calculation result": "Calculation result",
"True": "True", "True": "True",
"False": "False", "False": "False",
"concat": "concat",
"Condition": "Condition", "Condition": "Condition",
"Mode": "Mode", "Mode": "Mode",
"Continue when \"Yes\"": "Continue when \"Yes\"", "Continue when \"Yes\"": "Continue when \"Yes\"",
"Branch into \"Yes\" and \"No\"": "Branch into \"Yes\" and \"No\"", "Branch into \"Yes\" and \"No\"": "Branch into \"Yes\" and \"No\"",
"Conditions": "Conditions", "Conditions": "Conditions",
"Parallel branch": "Parallel branch", "Parallel branch": "Parallel branch",
"Add branch": "Add branch",
"All succeeded": "All succeeded", "All succeeded": "All succeeded",
"Any succeeded": "Any 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 all branches succeeded": "Continue after all branches succeeded",
"Continue after any branch succeeded": "Continue after any branch 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", "Create record": "Create record",
"Update record": "Update record", "Update record": "Update record",
"Query record": "Query 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.", "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", "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", "Node in executed workflow cannot be modified": "Node in executed workflow cannot be modified",
"Can not delete": "Can not delete",
'HTTP request': 'HTTP request', "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.",
'URL': 'URL', "HTTP request": "HTTP request",
'You can use the above available variables in URL.': 'You can use the above available variables in URL.', "URL": "URL",
'Request headers': 'Request headers', "You can use the above available variables in URL.": "You can use the above available variables in URL.",
'Name(e.g. Content-Type)': 'Name(e.g. Content-Type)', "Request headers": "Request headers",
'Value(e.g. Application/json)': 'Value(e.g. Application/json)', "Name(e.g. Content-Type)": "Name(e.g. Content-Type)",
'Add request header': 'Add request header', "Value(e.g. Application/json)": "Value(e.g. Application/json)",
'HTTP method': 'HTTP method', "Add request header": "Add request header",
'Request data': 'Request data', "HTTP method": "HTTP method",
'Input request data': 'Input request data', "Request data": "Request data",
'You can use the above available variables in request data.': 'You can use the above available variables in request data.', "Input request data": "Input request data",
'Copy success!': 'Copy success!', "You can use the above available variables in request data.": "You can use the above available variables in request data.",
'Copy variable output template statement': 'Copy variable output template statement', "Copy success!": "Copy success!",
'Default headers is Content-Type: application/json': 'Default headers is Content-Type: application/json', "Copy variable output template statement": "Copy variable output template statement",
'Ignore fail request and continue workflow': 'Ignore fail request and continue workflow', "Default headers is Content-Type: application/json": "Default headers is Content-Type: application/json",
'Syntax see': 'Syntax see', "Ignore fail request and continue workflow": "Ignore fail request and continue workflow",
'Show available variable tool': 'Show available variable tool' "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 { 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'; export const NAMESPACE = 'workflow';
i18n.addResources('zh-CN', NAMESPACE, zhCN); // i18n.addResources('zh-CN', NAMESPACE, zhCN);
i18n.addResources('en-US', NAMESPACE, enUS); // i18n.addResources('en-US', NAMESPACE, enUS);
i18n.addResources('ja-JP', NAMESPACE, jaJP); // i18n.addResources('ja-JP', NAMESPACE, jaJP);
i18n.addResources('ru-RU', NAMESPACE, ruRU); // i18n.addResources('ru-RU', NAMESPACE, ruRU);
i18n.addResources('tr-TR', NAMESPACE, trTR); // i18n.addResources('tr-TR', NAMESPACE, trTR);
export function lang(key: string, options = {}) { export function lang(key: string, options = {}) {
return i18n.t(key, { ...options, ns: NAMESPACE }); return i18n.t(key, { ...options, ns: NAMESPACE });

View File

@ -3597,7 +3597,7 @@
dependencies: dependencies:
regenerator-runtime "^0.13.2" 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" version "7.16.3"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5"
integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==
@ -3646,6 +3646,13 @@
dependencies: dependencies:
regenerator-runtime "^0.13.11" 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": "@babel/template@^7.10.4", "@babel/template@^7.16.7", "@babel/template@^7.4.0", "@babel/template@^7.4.4":
version "7.16.7" version "7.16.7"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
@ -9980,6 +9987,13 @@ cross-env@^7.0.3:
dependencies: dependencies:
cross-spawn "^7.0.1" 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: cross-spawn-async@^2.1.1:
version "2.2.5" version "2.2.5"
resolved "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz#845ff0c0834a3ded9d160daca6d390906bb288cc" 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: dependencies:
ms "^2.0.0" ms "^2.0.0"
i18next@^21.6.0: i18next-http-backend@^2.1.1:
version "21.6.0" version "2.1.1"
resolved "https://registry.npmjs.org/i18next/-/i18next-21.6.0.tgz#257abf455b24136640a20728b44cf59f60cdeb5c" resolved "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.1.1.tgz#72a21d61c2e96eea9ad45ba1b9dd0090e119709a"
integrity sha512-RjNuACL35wWZgtkyMcjcCmK7R72u3P6jTNbGKzrvHGI9M0iK5Vn1DsBIwOByppaXLIbe0viJ79Nz2h8w1UwPoQ== integrity sha512-jByfUCDVgQ8+/Wens7queQhYYvMcGTW/lR4IJJNEDDXnmqjLrwi8ubXKpmp76/JIWEZHffNdWqnxFJcTVGeaOw==
dependencies: 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: iconv-lite@0.4.24, iconv-lite@^0.4.15, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
version "0.4.24" 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" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== 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: node-fetch@^2.6.1:
version "2.6.6" version "2.6.6"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
@ -17448,13 +17476,6 @@ node-fetch@^2.6.1:
dependencies: dependencies:
whatwg-url "^5.0.0" 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: node-gyp@8.x:
version "8.4.1" version "8.4.1"
resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937"