feat: locale tester (#6576)

* feat: locale tester

* fix: locale

* fix: acl.registerSnippet

* feat: package.json

* fix: locale
This commit is contained in:
chenos 2025-03-28 12:39:44 +08:00 committed by GitHub
parent 9eaa47863d
commit 6bb754612c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 529 additions and 0 deletions

View File

@ -0,0 +1,2 @@
/node_modules
/src

View File

@ -0,0 +1 @@
# @nocobase/plugin-locale-tester

View File

@ -0,0 +1,2 @@
export * from './dist/client';
export { default } from './dist/client';

View File

@ -0,0 +1 @@
module.exports = require('./dist/client/index.js');

View File

@ -0,0 +1,14 @@
{
"name": "@nocobase/plugin-locale-tester",
"displayName": "Locale tester",
"displayName.zh-CN": "翻译测试工具",
"version": "1.6.12",
"homepage": "https://github.com/nocobase/locales",
"main": "dist/server/index.js",
"dependencies": {},
"peerDependencies": {
"@nocobase/client": "1.x",
"@nocobase/server": "1.x",
"@nocobase/test": "1.x"
}
}

View File

@ -0,0 +1,2 @@
export * from './dist/server';
export { default } from './dist/server';

View File

@ -0,0 +1 @@
module.exports = require('./dist/server/index.js');

View File

@ -0,0 +1,249 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
// CSS modules
type CSSModuleClasses = { readonly [key: string]: string };
declare module '*.module.css' {
const classes: CSSModuleClasses;
export default classes;
}
declare module '*.module.scss' {
const classes: CSSModuleClasses;
export default classes;
}
declare module '*.module.sass' {
const classes: CSSModuleClasses;
export default classes;
}
declare module '*.module.less' {
const classes: CSSModuleClasses;
export default classes;
}
declare module '*.module.styl' {
const classes: CSSModuleClasses;
export default classes;
}
declare module '*.module.stylus' {
const classes: CSSModuleClasses;
export default classes;
}
declare module '*.module.pcss' {
const classes: CSSModuleClasses;
export default classes;
}
declare module '*.module.sss' {
const classes: CSSModuleClasses;
export default classes;
}
// CSS
declare module '*.css' { }
declare module '*.scss' { }
declare module '*.sass' { }
declare module '*.less' { }
declare module '*.styl' { }
declare module '*.stylus' { }
declare module '*.pcss' { }
declare module '*.sss' { }
// Built-in asset types
// see `src/node/constants.ts`
// images
declare module '*.apng' {
const src: string;
export default src;
}
declare module '*.png' {
const src: string;
export default src;
}
declare module '*.jpg' {
const src: string;
export default src;
}
declare module '*.jpeg' {
const src: string;
export default src;
}
declare module '*.jfif' {
const src: string;
export default src;
}
declare module '*.pjpeg' {
const src: string;
export default src;
}
declare module '*.pjp' {
const src: string;
export default src;
}
declare module '*.gif' {
const src: string;
export default src;
}
declare module '*.svg' {
const src: string;
export default src;
}
declare module '*.ico' {
const src: string;
export default src;
}
declare module '*.webp' {
const src: string;
export default src;
}
declare module '*.avif' {
const src: string;
export default src;
}
// media
declare module '*.mp4' {
const src: string;
export default src;
}
declare module '*.webm' {
const src: string;
export default src;
}
declare module '*.ogg' {
const src: string;
export default src;
}
declare module '*.mp3' {
const src: string;
export default src;
}
declare module '*.wav' {
const src: string;
export default src;
}
declare module '*.flac' {
const src: string;
export default src;
}
declare module '*.aac' {
const src: string;
export default src;
}
declare module '*.opus' {
const src: string;
export default src;
}
declare module '*.mov' {
const src: string;
export default src;
}
declare module '*.m4a' {
const src: string;
export default src;
}
declare module '*.vtt' {
const src: string;
export default src;
}
// fonts
declare module '*.woff' {
const src: string;
export default src;
}
declare module '*.woff2' {
const src: string;
export default src;
}
declare module '*.eot' {
const src: string;
export default src;
}
declare module '*.ttf' {
const src: string;
export default src;
}
declare module '*.otf' {
const src: string;
export default src;
}
// other
declare module '*.webmanifest' {
const src: string;
export default src;
}
declare module '*.pdf' {
const src: string;
export default src;
}
declare module '*.txt' {
const src: string;
export default src;
}
// wasm?init
declare module '*.wasm?init' {
const initWasm: (options?: WebAssembly.Imports) => Promise<WebAssembly.Instance>;
export default initWasm;
}
// web worker
declare module '*?worker' {
const workerConstructor: {
new(options?: { name?: string }): Worker;
};
export default workerConstructor;
}
declare module '*?worker&inline' {
const workerConstructor: {
new(options?: { name?: string }): Worker;
};
export default workerConstructor;
}
declare module '*?worker&url' {
const src: string;
export default src;
}
declare module '*?sharedworker' {
const sharedWorkerConstructor: {
new(options?: { name?: string }): SharedWorker;
};
export default sharedWorkerConstructor;
}
declare module '*?sharedworker&inline' {
const sharedWorkerConstructor: {
new(options?: { name?: string }): SharedWorker;
};
export default sharedWorkerConstructor;
}
declare module '*?sharedworker&url' {
const src: string;
export default src;
}
declare module '*?raw' {
const src: string;
export default src;
}
declare module '*?url' {
const src: string;
export default src;
}
declare module '*?inline' {
const src: string;
export default src;
}

View File

@ -0,0 +1,124 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { useForm } from '@formily/react';
import { ActionProps, ISchema, Plugin, SchemaComponent, useAPIClient, useApp, useRequest } from '@nocobase/client';
import { Alert, App as AntdApp, Card, Spin } from 'antd';
import React from 'react';
import { useT } from './locale';
function LocaleTester() {
const { data, loading } = useRequest<any>({
url: 'localeTester:get',
});
const t = useT();
const schema: ISchema = {
type: 'void',
name: 'root',
properties: {
test: {
type: 'void',
'x-component': 'FormV2',
properties: {
locale: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input.JSON',
'x-component-props': {
autoSize: { minRows: 20, maxRows: 30 },
},
default: data?.data?.locale,
title: t('Translations'),
},
button: {
type: 'void',
'x-component': 'Action',
title: t('Submit'),
'x-use-component-props': 'useSubmitActionProps',
},
},
},
},
};
const useSubmitActionProps = () => {
const form = useForm();
const api = useAPIClient();
const { message } = AntdApp.useApp();
const app = useApp();
return {
type: 'primary',
htmlType: 'submit',
async onClick() {
await form.submit();
const values = form.values;
await api.request({
url: 'localeTester:updateOrCreate',
method: 'post',
params: {
filterKeys: ['id'],
},
data: {
id: data?.data?.id,
locale: values.locale,
},
});
message.success(app.i18n.t('Saved successfully!'));
window.location.reload();
},
};
};
if (loading) {
return <Spin />;
}
return (
<Card>
<Alert
style={{ marginBottom: 12 }}
description={
<div
dangerouslySetInnerHTML={{
__html: t(
`Please go to <a target="_blank" href="https://github.com/nocobase/locales">nocobase/locales</a> to get the language file that needs translation, then paste it below and provide the translation.`,
),
}}
></div>
}
/>
<SchemaComponent schema={schema} scope={{ useSubmitActionProps }} />
</Card>
);
}
export class PluginLocaleTesterClient extends Plugin {
async afterAdd() {
// await this.app.pm.add()
}
async beforeLoad() {}
// You can get and modify the app instance here
async load() {
this.app.pluginSettingsManager.add('locale-tester', {
title: this.t('Locale tester'),
icon: 'TranslationOutlined',
Component: LocaleTester,
});
// this.app.addComponents({})
// this.app.addScopes({})
// this.app.addProvider()
// this.app.addProviders()
// this.app.router.add()
}
}
export default PluginLocaleTesterClient;

View File

@ -0,0 +1,21 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
// @ts-ignore
import pkg from './../../package.json';
import { useApp } from '@nocobase/client';
export function useT() {
const app = useApp();
return (str: string) => app.i18n.t(str, { ns: [pkg.name, 'client'] });
}
export function tStr(key: string) {
return `{{t(${JSON.stringify(key)}, { ns: ['${pkg.name}', 'client'], nsMode: 'fallback' })}}`;
}

View File

@ -0,0 +1,11 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
export * from './server';
export { default } from './server';

View File

@ -0,0 +1,5 @@
{
"Locale": "Locale",
"Locale tester": "Locale tester",
"Please go to <a target=\"_blank\" href=\"https://github.com/nocobase/locales\">nocobase/locales</a> to get the language file that needs translation, then paste it below and provide the translation.": "Please go to <a target=\"_blank\" href=\"https://github.com/nocobase/locales\">nocobase/locales</a> to get the language file that needs translation, then paste it below and provide the translation."
}

View File

@ -0,0 +1,5 @@
{
"Translations": "翻译",
"Locale tester": "翻译测试工具",
"Please go to <a target=\"_blank\" href=\"https://github.com/nocobase/locales\">nocobase/locales</a> to get the language file that needs translation, then paste it below and provide the translation.": "请前往 <a target=\"_blank\" href=\"https://github.com/nocobase/locales\">nocobase/locales</a> 获取需要翻译的语言文件,粘贴到下方并进行翻译。"
}

View File

@ -0,0 +1,21 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { defineCollection } from '@nocobase/database';
export default defineCollection({
name: 'localeTester',
autoGenId: true,
fields: [
{
type: 'json',
name: 'locale',
},
],
});

View File

@ -0,0 +1,10 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
export { default } from './plugin';

View File

@ -0,0 +1,59 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { Plugin } from '@nocobase/server';
import _ from 'lodash';
export class PluginLocaleTesterServer extends Plugin {
async afterAdd() {}
async beforeLoad() {
this.app.acl.registerSnippet({
name: `pm.${this.name}`,
actions: ['localeTester:*'],
});
}
async load() {
this.app.resourceManager.use(async (ctx, next) => {
await next();
const { resourceName, actionName } = ctx.action;
if (resourceName === 'app' && actionName === 'getLang') {
const repository = this.db.getRepository('localeTester');
const record = await repository.findOne();
const locale = record?.locale || {};
if (locale['cronstrue']) {
_.set(ctx.body, 'cronstrue', locale['cronstrue']);
}
if (locale['react-js-cron']) {
_.set(ctx.body, 'cron', locale['react-js-cron']);
}
Object.keys(locale).forEach((key) => {
if (key === 'cronstrue' || key === 'react-js-cron') {
return;
}
const value = locale[key];
_.set(ctx.body, ['resources', key], value);
const k = key.replace('@nocobase/', '').replace('@nocobase/plugin-', '');
_.set(ctx.body, ['resources', k], value);
});
}
});
}
async install() {}
async afterEnable() {}
async afterDisable() {}
async remove() {}
}
export default PluginLocaleTesterServer;

View File

@ -43,6 +43,7 @@
"@nocobase/plugin-gantt": "1.6.12", "@nocobase/plugin-gantt": "1.6.12",
"@nocobase/plugin-graph-collection-manager": "1.6.12", "@nocobase/plugin-graph-collection-manager": "1.6.12",
"@nocobase/plugin-kanban": "1.6.12", "@nocobase/plugin-kanban": "1.6.12",
"@nocobase/plugin-locale-tester": "1.6.12",
"@nocobase/plugin-localization": "1.6.12", "@nocobase/plugin-localization": "1.6.12",
"@nocobase/plugin-logger": "1.6.12", "@nocobase/plugin-logger": "1.6.12",
"@nocobase/plugin-map": "1.6.12", "@nocobase/plugin-map": "1.6.12",