support language translation.
This commit is contained in:
@@ -8,9 +8,9 @@ import expressionParser from 'docxtemplater/expressions';
|
||||
import { saveAs } from 'file-saver';
|
||||
import moize from 'moize';
|
||||
import PizZip from 'pizzip';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import invariant from 'tiny-invariant';
|
||||
import { DOCX_CONTENT_TYPE } from '../common/constants';
|
||||
import { LANGUAGE } from '../common/kintoneUtils';
|
||||
import { KintoneFormFieldProperties } from '../common/types';
|
||||
import KintonePluginAlert from '../common/ui/KintonePluginAlert';
|
||||
import KintonePluginButton from '../common/ui/KintonePluginButton';
|
||||
@@ -26,7 +26,7 @@ interface MenuPanelProps {
|
||||
|
||||
const cachedFormFieldsProperties = moize.promise(async (appId: number): Promise<KintoneFormFieldProperties> => {
|
||||
const client = new KintoneRestAPIClient();
|
||||
const { properties } = await client.app.getFormFields({ app: appId, lang: 'en', preview: false });
|
||||
const { properties } = await client.app.getFormFields({ app: appId, preview: false });
|
||||
return properties;
|
||||
});
|
||||
|
||||
@@ -72,7 +72,11 @@ const formatNumberRecordValue = (
|
||||
return formatValueWithUnit(formattedValue, property);
|
||||
};
|
||||
|
||||
const formatCalculatedRecordValue = (value: string, property: KintoneFormFieldProperty.Calc): string => {
|
||||
const formatCalculatedRecordValue = (
|
||||
value: string,
|
||||
property: KintoneFormFieldProperty.Calc,
|
||||
language: string,
|
||||
): string => {
|
||||
const { format } = property;
|
||||
if (value === '') {
|
||||
return '';
|
||||
@@ -108,7 +112,7 @@ const formatCalculatedRecordValue = (value: string, property: KintoneFormFieldPr
|
||||
!isNaN(hours) && !isNaN(minutes),
|
||||
`Expected hours and minutes to be numbers, but got hours: ${hours}, minutes: ${minutes}`,
|
||||
);
|
||||
formattedValue = LANGUAGE === 'ja' ? `${hours}時間${minutes}分` : `${hours} hours ${minutes} minutes`;
|
||||
formattedValue = language === 'ja' ? `${hours}時間${minutes}分` : `${hours} hours ${minutes} minutes`;
|
||||
} else if (format === 'DAY_HOUR_MINUTE') {
|
||||
// "49:30" -> en: "2 days 1 hour 30 minutes", ja: "2日1時間30分
|
||||
const [hours, minutes] = value.split(':').map((v) => Number(v));
|
||||
@@ -119,7 +123,7 @@ const formatCalculatedRecordValue = (value: string, property: KintoneFormFieldPr
|
||||
const days = Math.floor(Number(hours) / 24);
|
||||
const remainingHours = Number(hours) % 24;
|
||||
formattedValue =
|
||||
LANGUAGE === 'ja'
|
||||
language === 'ja'
|
||||
? `${days}日${remainingHours}時間${minutes}分`
|
||||
: `${days} days ${remainingHours} hours ${minutes} minutes`;
|
||||
} else {
|
||||
@@ -129,7 +133,11 @@ const formatCalculatedRecordValue = (value: string, property: KintoneFormFieldPr
|
||||
return formatValueWithUnit(formattedValue, property);
|
||||
};
|
||||
|
||||
const record2data = (properties: KintoneFormFieldProperties, record: Partial<KintoneRecord>): TemplateData => {
|
||||
const record2data = (
|
||||
properties: KintoneFormFieldProperties,
|
||||
record: Partial<KintoneRecord>,
|
||||
language: string,
|
||||
): TemplateData => {
|
||||
const data: TemplateData = {};
|
||||
for (const key in record) {
|
||||
if (Object.prototype.hasOwnProperty.call(record, key) && Object.prototype.hasOwnProperty.call(properties, key)) {
|
||||
@@ -149,7 +157,7 @@ const record2data = (properties: KintoneFormFieldProperties, record: Partial<Kin
|
||||
data[key] = formatNumberRecordValue(value, property);
|
||||
} else if (type === 'CALC') {
|
||||
invariant(property.type === 'CALC', `Expected property type to be CALC, but got ${property.type}`);
|
||||
data[key] = formatCalculatedRecordValue(value, property);
|
||||
data[key] = formatCalculatedRecordValue(value, property, language);
|
||||
} else if (type === 'CHECK_BOX' || type === 'MULTI_SELECT' || type === 'CATEGORY') {
|
||||
data[key] = value.map((v) => v);
|
||||
} else if (
|
||||
@@ -163,7 +171,7 @@ const record2data = (properties: KintoneFormFieldProperties, record: Partial<Kin
|
||||
});
|
||||
} else if (type === 'SUBTABLE') {
|
||||
invariant(property.type === 'SUBTABLE', `Expected property type to be SUBTABLE, but got ${property.type}`);
|
||||
data[key] = value.map((subRecord) => record2data(property.fields, subRecord.value));
|
||||
data[key] = value.map((subRecord) => record2data(property.fields, subRecord.value, language));
|
||||
} else if (type === 'FILE') {
|
||||
invariant(property.type === 'FILE', `Expected property type to be FILE, but got ${property.type}`);
|
||||
data[key] = value.map((file) => ({
|
||||
@@ -184,31 +192,46 @@ const record2data = (properties: KintoneFormFieldProperties, record: Partial<Kin
|
||||
|
||||
const MenuPanel: React.FC<MenuPanelProps> = (props) => {
|
||||
const { pluginId, event } = props;
|
||||
const { t } = useTranslation();
|
||||
const appId = event.appId;
|
||||
const properties = React.use(cachedFormFieldsProperties(appId));
|
||||
const language = kintone.getLoginUser().language;
|
||||
const config = kintone.plugin.app.getConfig(pluginId);
|
||||
const template: string = config.template ?? '';
|
||||
|
||||
if (template === '') {
|
||||
return (
|
||||
<KintonePluginAlert>
|
||||
Word output plugin: Template field is not set. Please configure the plugin.
|
||||
{t('name')}: {t('errors.template-field-is-not-set')}
|
||||
</KintonePluginAlert>
|
||||
);
|
||||
}
|
||||
const record = event.record[template];
|
||||
if (record == null) {
|
||||
return <KintonePluginAlert>Word output plugin: Template field is not available in this app.</KintonePluginAlert>;
|
||||
return (
|
||||
<KintonePluginAlert>
|
||||
{t('name')}: {t('errors.template-field-is-not-available')}
|
||||
</KintonePluginAlert>
|
||||
);
|
||||
}
|
||||
if (record.type !== 'FILE') {
|
||||
return <KintonePluginAlert>Word output plugin: Template field must be a file field.</KintonePluginAlert>;
|
||||
return (
|
||||
<KintonePluginAlert>
|
||||
{t('name')}: {t('errors.template-field-must-be-an-attachment-field')}
|
||||
</KintonePluginAlert>
|
||||
);
|
||||
}
|
||||
if (record.value.length === 0) {
|
||||
return <KintonePluginAlert>Word output plugin: Template field does not contain any files.</KintonePluginAlert>;
|
||||
return (
|
||||
<KintonePluginAlert>
|
||||
{t('name')}: {t('errors.template-field-does-not-contain-any-files')}
|
||||
</KintonePluginAlert>
|
||||
);
|
||||
}
|
||||
if (record.value.length > 1) {
|
||||
return (
|
||||
<KintonePluginAlert>
|
||||
Word output plugin: Template field contains multiple files. Please ensure it contains only one file.
|
||||
{t('name')}: {t('errors.template-field-contains-multiple-files')}
|
||||
</KintonePluginAlert>
|
||||
);
|
||||
}
|
||||
@@ -216,7 +239,7 @@ const MenuPanel: React.FC<MenuPanelProps> = (props) => {
|
||||
if (contentType !== DOCX_CONTENT_TYPE) {
|
||||
return (
|
||||
<KintonePluginAlert>
|
||||
Word output plugin: The template file must be a DOCX file. The current file type is {contentType}.
|
||||
{t('name')}: {t('errors.template-file-must-be-a-docx', { contentType })}
|
||||
</KintonePluginAlert>
|
||||
);
|
||||
}
|
||||
@@ -234,7 +257,7 @@ const MenuPanel: React.FC<MenuPanelProps> = (props) => {
|
||||
parser: expressionParser,
|
||||
nullGetter: () => '',
|
||||
});
|
||||
doc.render(record2data(properties, event.record));
|
||||
doc.render(record2data(properties, event.record, language));
|
||||
const out = doc.getZip().generate({ type: 'blob', mimeType: DOCX_CONTENT_TYPE });
|
||||
saveAs(out, 'output.docx');
|
||||
})
|
||||
@@ -246,7 +269,7 @@ const MenuPanel: React.FC<MenuPanelProps> = (props) => {
|
||||
|
||||
return (
|
||||
<KintonePluginButton variant="normal" onClick={handleOnClickOutputButton}>
|
||||
Word出力
|
||||
{t('buttons.output')}
|
||||
</KintonePluginButton>
|
||||
);
|
||||
};
|
||||
|
Reference in New Issue
Block a user