Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2026-01-23T15:57:17.220Z\n"
"PO-Revision-Date: 2026-01-23T15:57:17.220Z\n"
"POT-Creation-Date: 2026-01-27T12:15:36.295Z\n"
"PO-Revision-Date: 2026-01-27T12:15:36.295Z\n"

msgid "schemas"
msgstr "schemas"
Expand Down Expand Up @@ -3199,8 +3199,8 @@ msgstr "No indicators available. Remove one from source."
msgid "What should happen to the source indicators after the merge is complete?"
msgstr "What should happen to the source indicators after the merge is complete?"

msgid "Country (required)"
msgstr "Country (required)"
msgid "Country"
msgstr "Country"

msgid "Failed to load countries"
msgstr "Failed to load countries"
Expand All @@ -3220,6 +3220,15 @@ msgstr "Select a language"
msgid "Locale will not be editable after it has been created."
msgstr "Locale will not be editable after it has been created."

msgid "Script"
msgstr "Script"

msgid "Four-letter script code in title case (e.g., Latn, Cyrl, Arab)"
msgstr "Four-letter script code in title case (e.g., Latn, Cyrl, Arab)"

msgid "e.g., Latn"
msgstr "e.g., Latn"

msgid "Set up the basic information for this option group set."
msgstr "Set up the basic information for this option group set."

Expand Down
9 changes: 7 additions & 2 deletions src/pages/locales/New.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ import { initialValues, validate } from './form/LocaleSchema'
const section = SECTIONS_MAP.locale

const useOnSubmitLocale = (): EnhancedOnSubmit<
ModelWithAttributeValues & { language?: string; country?: string }
ModelWithAttributeValues & {
language?: string
country?: string
script?: string
}
> => {
const dataEngine = useDataEngine()
const queryClient = useQueryClient()
Expand All @@ -47,7 +51,7 @@ const useOnSubmitLocale = (): EnhancedOnSubmit<
return
}

const { language, country } = values
const { language, country, script } = values

try {
await dataEngine.mutate({
Expand All @@ -56,6 +60,7 @@ const useOnSubmitLocale = (): EnhancedOnSubmit<
params: {
country,
language,
script,
},
data: {},
})
Expand Down
3 changes: 1 addition & 2 deletions src/pages/locales/form/CountryField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ export function CountryField() {
<Field
name="country"
dataTest="formfields-country"
label={i18n.t('Country (required)')}
required
label={i18n.t('Country')}
error={touched && !!error}
validationText={(touched && error?.toString()) || ''}
>
Expand Down
5 changes: 5 additions & 0 deletions src/pages/locales/form/LocaleFormFields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '../../../components'
import { CountryField } from './CountryField'
import { LanguageField } from './LanguageField'
import { ScriptField } from './ScriptField'

export const LocaleFormFields = () => {
return (
Expand All @@ -31,6 +32,10 @@ export const LocaleFormFields = () => {
<StandardFormField>
<CountryField />
</StandardFormField>

<StandardFormField>
<ScriptField />
</StandardFormField>
</StandardFormSection>
</>
)
Expand Down
25 changes: 23 additions & 2 deletions src/pages/locales/form/LocaleSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,31 @@ import { getDefaults, createFormValidate } from '../../../lib'

const localeBaseSchema = z.object({
language: z.string(),
country: z.string(),
country: z.string().optional(),
script: z
.string()
.length(4, 'Script must be exactly 4 characters')
.regex(
/^[A-Z][a-z]{3}$/,
'Script must be title case (e.g., Latn, Cyrl)'
)
.optional(),
})

export const localeFormSchema = localeBaseSchema
export const localeFormSchema = localeBaseSchema.refine(
(data) => {
// If script is provided, country must also be provided
if (data.script && !data.country) {
return false
}
return true
},
{
message: 'Country is required when Script is provided',
path: ['country'],
}
)

export const localeListSchema = localeBaseSchema.extend({
lastUpdated: z.coerce.date(),
created: z.coerce.date(),
Expand Down
3 changes: 3 additions & 0 deletions src/pages/locales/form/ScriptField.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.inputContainer {
width: 500px;
}
45 changes: 45 additions & 0 deletions src/pages/locales/form/ScriptField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import i18n from '@dhis2/d2-i18n'
import { Field, Input } from '@dhis2/ui'
import React from 'react'
import { useField } from 'react-final-form'
import classes from './ScriptField.module.css'

export function ScriptField() {
const {
input: { value, onChange, onBlur },
meta: { error, touched },
} = useField<string | undefined>('script')

const handleChange = ({ value }: { value?: string }) => {
onChange(value || '')
}

const handleBlur = () => {
onBlur()
}

return (
<Field
name="script"
dataTest="formfields-script"
label={i18n.t('Script')}
error={touched && !!error}
validationText={(touched && error?.toString()) || ''}
helpText={i18n.t(
'Four-letter script code in title case (e.g., Latn, Cyrl, Arab)'
)}
>
<div className={classes.inputContainer}>
<Input
name="script"
value={value || ''}
onChange={handleChange}
onBlur={handleBlur}
error={touched && !!error}
placeholder={i18n.t('e.g., Latn')}
dataTest="locale-script-field"
/>
</div>
</Field>
)
}
Loading