mirror of
https://github.com/Lifeforge-app/lifeforge.git
synced 2026-06-28 06:46:24 +00:00
feat(ui): allow namespace=false to skip i18n translation across components
This commit is contained in:
@@ -100,36 +100,6 @@ export const contract = {
|
||||
"NOT_FOUND": true
|
||||
}
|
||||
},
|
||||
"notifyMissing": {
|
||||
"method": "post",
|
||||
"description": "Report missing localization keys",
|
||||
"noAuth": false,
|
||||
"encrypted": true,
|
||||
"isDownloadable": false,
|
||||
"media": null,
|
||||
"input": {
|
||||
"body": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"namespace": {
|
||||
"type": "string"
|
||||
},
|
||||
"missingKey": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"namespace",
|
||||
"missingKey"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"NO_CONTENT": true
|
||||
}
|
||||
},
|
||||
"listUnsupportedModules": {
|
||||
"method": "get",
|
||||
"description": "List modules that do not support the user's currently selected language",
|
||||
@@ -299,9 +269,6 @@ export const contract = {
|
||||
"fontFamily": {
|
||||
"type": "string"
|
||||
},
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"dashboardLayout": {},
|
||||
"fontScale": {
|
||||
"type": "number"
|
||||
@@ -313,6 +280,9 @@ export const contract = {
|
||||
"bordered": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"created": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -328,9 +298,6 @@ export const contract = {
|
||||
"collectionName": {
|
||||
"type": "string"
|
||||
},
|
||||
"hasMasterPassword": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hasJournalMasterPassword": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -355,18 +322,17 @@ export const contract = {
|
||||
"bgImage",
|
||||
"backdropFilters",
|
||||
"fontFamily",
|
||||
"language",
|
||||
"dashboardLayout",
|
||||
"fontScale",
|
||||
"pinnedFontFamilies",
|
||||
"borderRadiusMultiplier",
|
||||
"bordered",
|
||||
"language",
|
||||
"created",
|
||||
"updated",
|
||||
"id",
|
||||
"collectionId",
|
||||
"collectionName",
|
||||
"hasMasterPassword",
|
||||
"hasJournalMasterPassword",
|
||||
"hasAPIKeysMasterPassword",
|
||||
"twoFAEnabled"
|
||||
|
||||
@@ -17,7 +17,7 @@ interface EmptyStateScreenProps {
|
||||
message:
|
||||
| {
|
||||
id: string
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
tKey?: string
|
||||
}
|
||||
| {
|
||||
@@ -77,16 +77,18 @@ export function EmptyStateScreen({
|
||||
weight="semibold"
|
||||
>
|
||||
{'id' in message
|
||||
? t(
|
||||
`${message.namespace ? `${message.namespace}:` : ''}${[
|
||||
message.tKey,
|
||||
'empty',
|
||||
message.id,
|
||||
'title'
|
||||
]
|
||||
.filter(e => e)
|
||||
.join('.')}`
|
||||
)
|
||||
? message.namespace === false
|
||||
? message.id
|
||||
: t(
|
||||
`${message.namespace ? `${message.namespace}:` : ''}${[
|
||||
message.tKey,
|
||||
'empty',
|
||||
message.id,
|
||||
'title'
|
||||
]
|
||||
.filter(e => e)
|
||||
.join('.')}`
|
||||
)
|
||||
: message.title}
|
||||
</Text>
|
||||
{(() => {
|
||||
@@ -122,16 +124,18 @@ export function EmptyStateScreen({
|
||||
}}
|
||||
whiteSpace="pre-wrap"
|
||||
>
|
||||
{t(
|
||||
`${message.namespace ? `${message.namespace}:` : ''}${[
|
||||
message.tKey,
|
||||
'empty',
|
||||
message.id,
|
||||
'description'
|
||||
]
|
||||
.filter(e => e)
|
||||
.join('.')}`
|
||||
)}
|
||||
{message.namespace === false
|
||||
? ''
|
||||
: t(
|
||||
`${message.namespace ? `${message.namespace}:` : ''}${[
|
||||
message.tKey,
|
||||
'empty',
|
||||
message.id,
|
||||
'description'
|
||||
]
|
||||
.filter(e => e)
|
||||
.join('.')}`
|
||||
)}
|
||||
</Text>
|
||||
)
|
||||
})()}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ModalHeader } from '@/components/overlays'
|
||||
import { Flex, Stack } from '@/components/primitives'
|
||||
import { toast } from '@/providers'
|
||||
|
||||
const NamespaceContext = createContext<string | undefined>(undefined)
|
||||
const NamespaceContext = createContext<string | false | undefined>(undefined)
|
||||
|
||||
export function useNamespace() {
|
||||
return useContext(NamespaceContext)
|
||||
@@ -50,7 +50,7 @@ export function FormModal<T extends FieldValues>({
|
||||
title: string | React.ReactNode
|
||||
icon: string
|
||||
onClose: () => void
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
loading?: boolean
|
||||
headerActions?: React.ReactNode
|
||||
}
|
||||
@@ -82,7 +82,7 @@ export function FormModal<T extends FieldValues>({
|
||||
<ModalHeader
|
||||
headerActions={headerActions}
|
||||
icon={icon}
|
||||
namespace={namespace ? namespace : undefined}
|
||||
namespace={namespace}
|
||||
title={title}
|
||||
onClose={onClose}
|
||||
/>
|
||||
|
||||
@@ -18,7 +18,7 @@ type CheckboxFieldProps<TFieldValues extends FieldValues> = {
|
||||
label: string
|
||||
icon: string
|
||||
disabled?: boolean
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
}
|
||||
|
||||
export function CheckboxField<TFieldValues extends FieldValues>({
|
||||
@@ -38,12 +38,17 @@ export function CheckboxField<TFieldValues extends FieldValues>({
|
||||
|
||||
const activeNamespace = namespace ?? contextNamespace
|
||||
|
||||
const { t } = useTranslation(activeNamespace)
|
||||
const { t } = useTranslation(
|
||||
activeNamespace === false ? undefined : activeNamespace
|
||||
)
|
||||
|
||||
const labelText = t([
|
||||
['inputs', _.camelCase(label), 'label'].filter(Boolean).join('.'),
|
||||
['inputs', _.camelCase(label)].filter(Boolean).join('.')
|
||||
])
|
||||
const labelText =
|
||||
activeNamespace === false
|
||||
? label
|
||||
: t([
|
||||
['inputs', _.camelCase(label), 'label'].filter(Boolean).join('.'),
|
||||
['inputs', _.camelCase(label)].filter(Boolean).join('.')
|
||||
])
|
||||
|
||||
function handleSwitchChange() {
|
||||
field.onChange(!field.value)
|
||||
|
||||
@@ -21,7 +21,7 @@ type FileFieldProps<TFieldValues extends FieldValues> = {
|
||||
reminderText?: string
|
||||
onImageRemoved?: () => void
|
||||
required?: boolean
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
disabled?: boolean
|
||||
sources?: FilePickerSourceConfig
|
||||
mimeTypes?: Record<string, string[]>
|
||||
|
||||
@@ -25,7 +25,7 @@ type ListboxFieldProps<TFieldValues extends FieldValues, TOption> = {
|
||||
label: string
|
||||
multiple?: boolean
|
||||
disabled?: boolean
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
required?: boolean
|
||||
options: ListboxOptionType<TOption>[]
|
||||
actionButtonOption?: {
|
||||
|
||||
@@ -17,7 +17,7 @@ type LocationFieldProps<TFieldValues extends FieldValues> = {
|
||||
required?: boolean
|
||||
disabled?: boolean
|
||||
autoFocus?: boolean
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
}
|
||||
|
||||
export function LocationField<TFieldValues extends FieldValues>({
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useNamespace } from '../FormModal'
|
||||
type SliderFieldProps<TFieldValues extends FieldValues> = {
|
||||
control: Control<TFieldValues>
|
||||
name: FieldPathByValue<TFieldValues, number | null | undefined>
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
} & Omit<SliderInputProps, 'value' | 'onChange'>
|
||||
|
||||
export function SliderField<TFieldValues extends FieldValues>({
|
||||
|
||||
@@ -39,7 +39,7 @@ type ButtonOwnProps = {
|
||||
/** Whether the button should be styled as dangerous/destructive with a red background. */
|
||||
dangerous?: boolean
|
||||
/** The i18n namespace for internationalization. See the [main documentation](https://docs.lifeforge.melvinchia.dev) for more details. */
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
/** Additional properties for the translation function. Used for dynamic translations. See the [i18n documentation](https://docs.lifeforge.melvinchia.dev) for more details. */
|
||||
tProps?: Record<string, unknown>
|
||||
style?: CSSProperties
|
||||
@@ -84,7 +84,7 @@ export function Button<T extends ElementType = 'button'>({
|
||||
props: props as any
|
||||
})
|
||||
|
||||
const { t } = useModuleTranslation([namespace])
|
||||
const { t } = useModuleTranslation(namespace === false ? [] : [namespace])
|
||||
|
||||
return (
|
||||
<Transition>
|
||||
@@ -124,18 +124,22 @@ export function Button<T extends ElementType = 'button'>({
|
||||
{children && typeof children === 'string' ? (
|
||||
<Box asChild minWidth="0">
|
||||
<Text truncate>
|
||||
{t(
|
||||
[
|
||||
`buttons.${_.camelCase(children)}`,
|
||||
`${_.camelCase(children)}`,
|
||||
children,
|
||||
`${namespace}:buttons.${_.camelCase(children)}`,
|
||||
`${namespace}:${_.camelCase(children)}`,
|
||||
`${namespace}:${children}`,
|
||||
`common.buttons:${_.camelCase(children)}`
|
||||
],
|
||||
{ ...tProps, defaultValue: children }
|
||||
)}
|
||||
{namespace === false
|
||||
? children
|
||||
: t(
|
||||
[
|
||||
`buttons.${_.camelCase(children)}`,
|
||||
`${_.camelCase(children)}`,
|
||||
children,
|
||||
`${namespace}:buttons.${_.camelCase(children)}`,
|
||||
`${namespace}:${_.camelCase(children)}`,
|
||||
`${namespace}:${children}`,
|
||||
`common.buttons:buttons.${_.camelCase(children)}`,
|
||||
`common.buttons:${_.camelCase(children)}`,
|
||||
`common.buttons:${children}`
|
||||
],
|
||||
{ ...tProps, defaultValue: children }
|
||||
)}
|
||||
</Text>
|
||||
</Box>
|
||||
) : (
|
||||
|
||||
@@ -30,7 +30,7 @@ export interface ColorInputProps {
|
||||
/** Additional CSS class names to apply to the color input component. */
|
||||
className?: string
|
||||
/** The i18n namespace for internationalization. See the [main documentation](https://docs.lifeforge.melvinchia.dev) for more details. */
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
/** Error message to display when the input is invalid. */
|
||||
errorMsg?: string
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ interface ComboboxInputProps<T> {
|
||||
/** Additional CSS class names to apply to the combobox. Use `!` suffix for Tailwind CSS class overrides. */
|
||||
className?: string
|
||||
/** The i18n namespace for internationalization. See the [main documentation](https://docs.lifeforge.melvinchia.dev) for more details. */
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
/** Error message to display when the input is invalid. */
|
||||
errorMsg?: string
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export type CurrencyInputProps = {
|
||||
/** Additional CSS class names to apply to the currency input component. */
|
||||
className?: string
|
||||
/** The i18n namespace for internationalization. Use false to disable translation. */
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
/** Error message to display when the input is invalid. */
|
||||
errorMsg?: string
|
||||
} & InputVariants
|
||||
|
||||
@@ -42,7 +42,7 @@ export interface DateInputProps {
|
||||
/** Whether the date input includes time selection. */
|
||||
hasTime?: boolean
|
||||
/** The i18n namespace for internationalization. See the [main documentation](https://docs.lifeforge.melvinchia.dev) for more details. */
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
/** Error message to display when the input is invalid. */
|
||||
errorMsg?: string
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ export function FileInput({
|
||||
onChange: (value: FileValue) => void
|
||||
onImageRemoved?: () => void
|
||||
required?: boolean
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
disabled?: boolean
|
||||
sources?: FilePickerSourceConfig
|
||||
mimeTypes?: Record<string, string[]>
|
||||
|
||||
@@ -22,7 +22,7 @@ export interface IconInputProps {
|
||||
required?: boolean
|
||||
disabled?: boolean
|
||||
autoFocus?: boolean
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
errorMsg?: string
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ interface ListboxInputProps<T> {
|
||||
/** The custom content to display in the listbox button. */
|
||||
renderContent?: (value: T) => React.ReactNode
|
||||
/** The i18n namespace for internationalization. See the [main documentation](https://docs.lifeforge.melvinchia.dev) for more details. */
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
/** The error message to display when the field is invalid. */
|
||||
errorMsg?: string
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ interface LocationInputProps {
|
||||
disabled?: boolean
|
||||
autoFocus?: boolean
|
||||
className?: string
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
errorMsg?: string
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface NumberInputProps {
|
||||
/** Additional CSS class names to apply to the number input component. */
|
||||
className?: string
|
||||
/** The i18n namespace for internationalization. See the [main documentation](https://docs.lifeforge.melvinchia.dev) for more details. */
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
/** Error message to display when the input is invalid. */
|
||||
errorMsg?: string
|
||||
/** The minimum value allowed. */
|
||||
|
||||
@@ -33,7 +33,7 @@ interface SearchInputProps extends Omit<FlexProps<'search'>, 'onChange'> {
|
||||
/** Additional CSS class names to apply to the search input container. */
|
||||
className?: string
|
||||
/** The i18n namespace for internationalization. See the [main documentation](https://docs.lifeforge.melvinchia.dev) for more details. */
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
/** Optional debounce delay in milliseconds. When set, the onChange callback will be debounced by this amount. */
|
||||
debounceMs?: number
|
||||
/** Policy for showing children components.
|
||||
@@ -71,10 +71,11 @@ export function SearchInput({
|
||||
children,
|
||||
...props
|
||||
}: SearchInputProps) {
|
||||
const { t } = useModuleTranslation([
|
||||
'common.misc',
|
||||
...(namespace ? [namespace] : [])
|
||||
])
|
||||
const { t } = useModuleTranslation(
|
||||
namespace === false
|
||||
? []
|
||||
: ['common.misc', ...(namespace ? [namespace] : [])]
|
||||
)
|
||||
// Internal state for immediate input feedback when debouncing
|
||||
|
||||
const [internalValue, setInternalValue] = useState(value)
|
||||
@@ -214,27 +215,31 @@ export function SearchInput({
|
||||
data-form-type="other"
|
||||
data-lpignore="true"
|
||||
disabled={disabled}
|
||||
placeholder={t([`common.misc:search`, `Search ${searchTarget}`], {
|
||||
item: t(
|
||||
[
|
||||
`items.${_.camelCase(searchTarget)}`,
|
||||
`items.${searchTarget}`,
|
||||
`${_.camelCase(searchTarget)}`,
|
||||
`${searchTarget}`,
|
||||
`${namespace}:items.${_.camelCase(searchTarget)}`,
|
||||
`${namespace}:items.${searchTarget}`,
|
||||
`${namespace}:${_.camelCase(searchTarget)}`,
|
||||
`${namespace}:${searchTarget}`,
|
||||
`common.misc:items.${_.camelCase(searchTarget)}`,
|
||||
`common.misc:items.${searchTarget}`,
|
||||
`common.misc:${_.camelCase(searchTarget)}`,
|
||||
`common.misc:${searchTarget}`
|
||||
],
|
||||
{
|
||||
defaultValue: searchTarget
|
||||
}
|
||||
)
|
||||
})}
|
||||
placeholder={
|
||||
namespace === false
|
||||
? `Search ${searchTarget}`
|
||||
: t([`common.misc:search`, `Search ${searchTarget}`], {
|
||||
item: t(
|
||||
[
|
||||
`items.${_.camelCase(searchTarget)}`,
|
||||
`items.${searchTarget}`,
|
||||
`${_.camelCase(searchTarget)}`,
|
||||
`${searchTarget}`,
|
||||
`${namespace}:items.${_.camelCase(searchTarget)}`,
|
||||
`${namespace}:items.${searchTarget}`,
|
||||
`${namespace}:${_.camelCase(searchTarget)}`,
|
||||
`${namespace}:${searchTarget}`,
|
||||
`common.misc:items.${_.camelCase(searchTarget)}`,
|
||||
`common.misc:items.${searchTarget}`,
|
||||
`common.misc:${_.camelCase(searchTarget)}`,
|
||||
`common.misc:${searchTarget}`
|
||||
],
|
||||
{
|
||||
defaultValue: searchTarget
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
style={{ paddingRight: actionButtonProps ? '5rem' : '2.5rem' }}
|
||||
type="text"
|
||||
value={displayValue}
|
||||
|
||||
@@ -11,7 +11,7 @@ export function SliderHeader({
|
||||
}: {
|
||||
icon?: string
|
||||
label?: string
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
value: number
|
||||
required?: boolean
|
||||
max?: number
|
||||
|
||||
@@ -20,7 +20,7 @@ export interface SliderInputProps extends Omit<
|
||||
max?: number
|
||||
step?: number
|
||||
className?: string
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
}
|
||||
|
||||
export function SliderInput({
|
||||
|
||||
@@ -50,7 +50,7 @@ interface TagsInputProps {
|
||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void | Promise<void>
|
||||
}
|
||||
/** The i18n namespace for internationalization. See the [main documentation](https://docs.lifeforge.melvinchia.dev) for more details. */
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
/** Error message to display when the input is invalid. */
|
||||
errorMsg?: string
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export type TextAreaInputProps = {
|
||||
/** Additional CSS class names to apply to the textarea element. Use `!` suffix for Tailwind CSS class overrides. */
|
||||
className?: string
|
||||
/** The i18n namespace for internationalization. See the [main documentation](https://docs.lifeforge.melvinchia.dev) for more details. */
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
/** Error message to display when the input is invalid. */
|
||||
errorMsg?: string
|
||||
} & InputVariants
|
||||
|
||||
@@ -34,7 +34,7 @@ export type TextInputProps = {
|
||||
| 'search'
|
||||
actionButtonProps?: React.ComponentProps<typeof Button>
|
||||
className?: string
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
errorMsg?: string
|
||||
inputRef?: React.RefObject<HTMLInputElement | null>
|
||||
} & Omit<React.HTMLAttributes<HTMLInputElement>, 'onChange'> &
|
||||
|
||||
@@ -6,11 +6,15 @@ export function useInputLabel({
|
||||
namespace,
|
||||
label
|
||||
}: {
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
label: string
|
||||
}) {
|
||||
const { t } = useModuleTranslation(namespace ? [namespace] : undefined)
|
||||
|
||||
if (namespace === false) {
|
||||
return label
|
||||
}
|
||||
|
||||
return t(
|
||||
[
|
||||
`inputs.${_.camelCase(label)}.label`,
|
||||
|
||||
@@ -23,7 +23,7 @@ interface ModuleHeaderProps {
|
||||
contextMenuProps?: React.ComponentProps<typeof ContextMenu>
|
||||
actionButton?: React.ReactNode
|
||||
customElement?: React.ReactNode
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
tKey?: string
|
||||
}
|
||||
|
||||
@@ -43,11 +43,11 @@ export function ModuleHeader({
|
||||
title = title ?? innerTitle
|
||||
icon = icon ?? innerIcon
|
||||
|
||||
const { t } = useModuleTranslation([
|
||||
`common.${title}`,
|
||||
'common.misc',
|
||||
namespace ?? ''
|
||||
])
|
||||
const { t } = useModuleTranslation(
|
||||
namespace === false
|
||||
? []
|
||||
: [`common.${title}`, 'common.misc', namespace ?? '']
|
||||
)
|
||||
|
||||
const { toggleSidebar, sidebarExpanded } = useMainSidebarState()
|
||||
|
||||
@@ -105,15 +105,17 @@ export function ModuleHeader({
|
||||
width="100%"
|
||||
>
|
||||
<Text truncate display="block">
|
||||
{t([
|
||||
`${namespace}:${tKey}.${title}.title`,
|
||||
`${namespace}:${title}.title`,
|
||||
`apps.${title}:title`,
|
||||
`common.${title}:title`,
|
||||
'common.misc:title',
|
||||
'title',
|
||||
title?.toString() ?? ''
|
||||
])}
|
||||
{namespace === false
|
||||
? (title?.toString() ?? '')
|
||||
: t([
|
||||
`${namespace}:${tKey}.${title}.title`,
|
||||
`${namespace}:${title}.title`,
|
||||
`apps.${title}:title`,
|
||||
`common.${title}:title`,
|
||||
'common.misc:title',
|
||||
'title',
|
||||
title?.toString() ?? ''
|
||||
])}
|
||||
</Text>
|
||||
<Box asChild minWidth="0">
|
||||
<Text
|
||||
@@ -135,15 +137,17 @@ export function ModuleHeader({
|
||||
size={{ base: 'sm', sm: 'base' }}
|
||||
whiteSpace="nowrap"
|
||||
>
|
||||
{t([
|
||||
`${namespace}:${tKey}.${title}.description`,
|
||||
`${namespace}:${title}.description`,
|
||||
`apps.${title}:description`,
|
||||
`common.${title}:description`,
|
||||
'common.misc:description',
|
||||
'description',
|
||||
`Description for ${title?.toString() ?? ''}`
|
||||
])}
|
||||
{namespace === false
|
||||
? `Description for ${title?.toString() ?? ''}`
|
||||
: t([
|
||||
`${namespace}:${tKey}.${title}.description`,
|
||||
`${namespace}:${title}.description`,
|
||||
`apps.${title}:description`,
|
||||
`common.${title}:description`,
|
||||
'common.misc:description',
|
||||
'description',
|
||||
`Description for ${title?.toString() ?? ''}`
|
||||
])}
|
||||
</Text>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
@@ -57,11 +57,13 @@ export function SidebarTitle({
|
||||
weight="semibold"
|
||||
whiteSpace="nowrap"
|
||||
>
|
||||
{t([
|
||||
`sidebar.${_.camelCase(label)}`,
|
||||
`common.sidebar:categories.${_.camelCase(label)}`,
|
||||
label
|
||||
])}
|
||||
{namespace === false
|
||||
? label
|
||||
: t([
|
||||
`sidebar.${_.camelCase(label)}`,
|
||||
`common.sidebar:categories.${_.camelCase(label)}`,
|
||||
label
|
||||
])}
|
||||
</Text>
|
||||
{actionButton && 'icon' in actionButton ? (
|
||||
<Text
|
||||
|
||||
@@ -44,7 +44,7 @@ interface ContextMenuItemProps {
|
||||
/** Additional CSS class names to apply to the menu item. */
|
||||
className?: string
|
||||
/** The i18n namespace for internationalization. See the [main documentation](https://docs.lifeforge.melvinchia.dev) for more details. */
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
/** Additional properties for the translation function. Used for dynamic translations. See the [i18n documentation](https://docs.lifeforge.melvinchia.dev) for more details. */
|
||||
tProps?: Record<string, unknown>
|
||||
/** Callback function called when the menu item is clicked. */
|
||||
@@ -64,7 +64,9 @@ export function ContextMenuItem({
|
||||
tProps,
|
||||
onClick
|
||||
}: ContextMenuItemProps) {
|
||||
const { t } = useModuleTranslation(namespace ? [namespace] : undefined)
|
||||
const { t } = useModuleTranslation(
|
||||
namespace ? [namespace] : undefined
|
||||
)
|
||||
|
||||
return (
|
||||
<WithDivide>
|
||||
@@ -130,7 +132,10 @@ export function ContextMenuItem({
|
||||
[
|
||||
_.camelCase(label),
|
||||
`buttons.${_.camelCase(label)}`,
|
||||
label
|
||||
label,
|
||||
`${namespace}:${_.camelCase(label)}`,
|
||||
`${namespace}:buttons.${_.camelCase(label)}`,
|
||||
`${namespace}:${label}`
|
||||
],
|
||||
tProps
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ function getLocaleKeys(innerTitle: string, namespace?: string) {
|
||||
`${innerTitle}.title`,
|
||||
`${innerTitle}`,
|
||||
`modals.${innerTitle}.title`,
|
||||
`modals.${innerTitle}`,
|
||||
`modals.${innerTitle}`
|
||||
].map(e => (namespace ? `${namespace}:${e}` : e))
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ function _ModalHeader({
|
||||
hasAI?: boolean
|
||||
className?: string
|
||||
appendTitle?: React.ReactElement
|
||||
namespace?: string
|
||||
namespace?: string | false
|
||||
headerActions?: React.ReactNode
|
||||
}) {
|
||||
const { t } = useModuleTranslation(namespace ? [namespace] : [])
|
||||
@@ -65,16 +65,20 @@ function _ModalHeader({
|
||||
{typeof innerTitle === 'string' ? (
|
||||
<>
|
||||
<Text truncate as="span" style={{ minWidth: 0 }}>
|
||||
{t(
|
||||
[
|
||||
...getLocaleKeys(innerTitle),
|
||||
...(namespace ? getLocaleKeys(innerTitle, namespace) : []),
|
||||
...getLocaleKeys(innerTitle, 'common.modals')
|
||||
],
|
||||
{
|
||||
defaultValue: innerTitle
|
||||
}
|
||||
)}
|
||||
{namespace === false
|
||||
? innerTitle
|
||||
: t(
|
||||
[
|
||||
...getLocaleKeys(innerTitle),
|
||||
...(namespace
|
||||
? getLocaleKeys(innerTitle, namespace)
|
||||
: []),
|
||||
...getLocaleKeys(innerTitle, 'common.modals')
|
||||
],
|
||||
{
|
||||
defaultValue: innerTitle
|
||||
}
|
||||
)}
|
||||
</Text>
|
||||
{appendTitle}
|
||||
{hasAI && (
|
||||
|
||||
Reference in New Issue
Block a user