Some better activity translations
This commit is contained in:
parent
cf01490883
commit
76472411e3
5 changed files with 34 additions and 13 deletions
|
@ -57,7 +57,16 @@ class LocaleController extends Controller
|
||||||
if (is_array($value)) {
|
if (is_array($value)) {
|
||||||
$data[$key] = $this->i18n($value);
|
$data[$key] = $this->i18n($value);
|
||||||
} else {
|
} else {
|
||||||
$data[$key] = preg_replace('/:([\w-]+)(\W?|$)/m', '{{$1}}$2', $value);
|
// Find a Laravel style translation replacement in the string and replace it with
|
||||||
|
// one that the front-end is able to use. This won't always be present, especially
|
||||||
|
// for complex strings or things where we'd never have a backend component anyways.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
// "Hello :name, the :notifications.0.title notification needs :count actions :foo.0.bar."
|
||||||
|
//
|
||||||
|
// Becomes:
|
||||||
|
// "Hello {{name}}, the {{notifications.0.title}} notification needs {{count}} actions {{foo.0.bar}}."
|
||||||
|
$data[$key] = preg_replace('/:([\w.-]+\w)([^\w:]?|$)/m', '{{$1}}$2', $value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ class ActivityLogTransformer extends BaseClientTransformer
|
||||||
}
|
}
|
||||||
|
|
||||||
$str = trans('activity.' . str_replace(':', '.', $model->event));
|
$str = trans('activity.' . str_replace(':', '.', $model->event));
|
||||||
preg_match_all('/:(?<key>[\w-]+)(?:\W?|$)/', $str, $matches);
|
preg_match_all('/:(?<key>[\w.-]+\w)(?:[^\w:]?|$)/', $str, $matches);
|
||||||
|
|
||||||
$exclude = array_merge($matches['key'], ['ip', 'useragent']);
|
$exclude = array_merge($matches['key'], ['ip', 'useragent']);
|
||||||
foreach ($model->properties->keys() as $key) {
|
foreach ($model->properties->keys() as $key) {
|
||||||
|
|
|
@ -54,17 +54,18 @@ return [
|
||||||
'delete' => 'Deleted database :name',
|
'delete' => 'Deleted database :name',
|
||||||
],
|
],
|
||||||
'file' => [
|
'file' => [
|
||||||
'compress_one' => 'Compressed :directory/:file',
|
'compress_one' => 'Compressed :directory:file',
|
||||||
'compress_other' => 'Compressed :count files in :directory',
|
'compress_other' => 'Compressed :count files in :directory',
|
||||||
'read' => 'Viewed the contents of :file',
|
'read' => 'Viewed the contents of :file',
|
||||||
'copy' => 'Created a copy of :file',
|
'copy' => 'Created a copy of :file',
|
||||||
'create-directory' => 'Created a new directory :name in :directory',
|
'create-directory' => 'Created a new directory :name in :directory',
|
||||||
'decompress' => 'Decompressed :files in :directory',
|
'decompress' => 'Decompressed :files in :directory',
|
||||||
'delete_one' => 'Deleted :directory/:files',
|
'delete_one' => 'Deleted :directory:files',
|
||||||
'delete_other' => 'Deleted :count files in :directory',
|
'delete_other' => 'Deleted :count files in :directory',
|
||||||
'download' => 'Downloaded :file',
|
'download' => 'Downloaded :file',
|
||||||
'pull' => 'Downloaded a remote file from :url to :directory',
|
'pull' => 'Downloaded a remote file from :url to :directory',
|
||||||
'rename' => 'Renamed files in :directory',
|
'rename_one' => 'Renamed :directory:files.0.from to :directory:files.0.to',
|
||||||
|
'rename_other' => 'Renamed :count files in :directory',
|
||||||
'write' => 'Wrote new content to :file',
|
'write' => 'Wrote new content to :file',
|
||||||
'upload' => 'Began a file upload',
|
'upload' => 'Began a file upload',
|
||||||
],
|
],
|
||||||
|
@ -75,7 +76,7 @@ return [
|
||||||
'delete' => 'Deleted the :allocation allocation',
|
'delete' => 'Deleted the :allocation allocation',
|
||||||
],
|
],
|
||||||
'schedule' => [
|
'schedule' => [
|
||||||
'store' => 'Created the :name schedule',
|
'create' => 'Created the :name schedule',
|
||||||
'update' => 'Updated the :name schedule',
|
'update' => 'Updated the :name schedule',
|
||||||
'execute' => 'Manually executed the :name schedule',
|
'execute' => 'Manually executed the :name schedule',
|
||||||
'delete' => 'Deleted the :name schedule',
|
'delete' => 'Deleted the :name schedule',
|
||||||
|
|
|
@ -10,12 +10,28 @@ import ActivityLogMetaButton from '@/components/elements/activity/ActivityLogMet
|
||||||
import { TerminalIcon } from '@heroicons/react/solid';
|
import { TerminalIcon } from '@heroicons/react/solid';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import style from './style.module.css';
|
import style from './style.module.css';
|
||||||
|
import { isObject } from '@/helpers';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
activity: ActivityLog;
|
activity: ActivityLog;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formatProperties = (properties: Record<string, unknown>): Record<string, unknown> => {
|
||||||
|
return Object.keys(properties).reduce((obj, key) => {
|
||||||
|
const value = properties[key];
|
||||||
|
// noinspection SuspiciousTypeOfGuard
|
||||||
|
const isCount = key === 'count' || (typeof key === 'string' && key.endsWith('_count'));
|
||||||
|
|
||||||
|
return {
|
||||||
|
...obj,
|
||||||
|
[key]: isCount || typeof value !== 'string'
|
||||||
|
? (isObject(value) ? formatProperties(value) : value)
|
||||||
|
: `<strong>${value}</strong>`,
|
||||||
|
};
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
export default ({ activity, children }: Props) => {
|
export default ({ activity, children }: Props) => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const actor = activity.relationships.actor;
|
const actor = activity.relationships.actor;
|
||||||
|
@ -27,12 +43,7 @@ export default ({ activity, children }: Props) => {
|
||||||
return current.toString();
|
return current.toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
const properties = Object.keys(activity.properties).reduce((obj, key) => ({
|
const properties = formatProperties(activity.properties);
|
||||||
...obj,
|
|
||||||
[key]: key === 'count' || key.endsWith('_count')
|
|
||||||
? activity.properties[key]
|
|
||||||
: `<strong>${activity.properties[key]}</strong>`,
|
|
||||||
}), {});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'grid grid-cols-10 py-4 border-b-2 border-gray-800 last:rounded-b last:border-0 group'}>
|
<div className={'grid grid-cols-10 py-4 border-b-2 border-gray-800 last:rounded-b last:border-0 group'}>
|
||||||
|
|
|
@ -74,4 +74,4 @@ export const isEmptyObject = (o: {}): boolean =>
|
||||||
Object.keys(o).length === 0 && Object.getPrototypeOf(o) === Object.prototype;
|
Object.keys(o).length === 0 && Object.getPrototypeOf(o) === Object.prototype;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
export const getObjectKeys = <T extends {}> (o: T): Array<keyof T> => Object.keys(o) as Array<keyof T>;
|
export const getObjectKeys = <T extends {}> (o: T): (keyof T)[] => Object.keys(o) as (keyof typeof o)[];
|
||||||
|
|
Loading…
Reference in a new issue