diff --git a/apps/backoffice/src/app/i18n/locales/am.ts b/apps/backoffice/src/app/i18n/locales/am.ts index 79f084307..37718d47c 100644 --- a/apps/backoffice/src/app/i18n/locales/am.ts +++ b/apps/backoffice/src/app/i18n/locales/am.ts @@ -262,6 +262,11 @@ export const am: Translations = { }, }, + country: { + select: 'አገር ይምረጡ', + notFound: 'ምንም አገር አልተገኘም', + }, + location: { title: 'አካባቢዎች', hierarchy: 'የአካባቢ ተዋረድ', diff --git a/apps/backoffice/src/app/i18n/locales/en.ts b/apps/backoffice/src/app/i18n/locales/en.ts index 9ad2adfd7..9783f2520 100644 --- a/apps/backoffice/src/app/i18n/locales/en.ts +++ b/apps/backoffice/src/app/i18n/locales/en.ts @@ -260,6 +260,11 @@ export const en = { }, }, + country: { + select: 'Select a country', + notFound: 'No countries found', + }, + location: { title: 'Locations', hierarchy: 'Location Hierarchy', diff --git a/apps/portal/src/app/features/endorsement/pages/EndorsementPage.tsx b/apps/portal/src/app/features/endorsement/pages/EndorsementPage.tsx index a92437be3..635c05c73 100644 --- a/apps/portal/src/app/features/endorsement/pages/EndorsementPage.tsx +++ b/apps/portal/src/app/features/endorsement/pages/EndorsementPage.tsx @@ -232,7 +232,7 @@ function ApplicationWizard({ onDone }: { onDone: () => void }) { {[ ['CoC Number', cocNo], - ['Country', getCountryName(country)], + ['Country', getCountryName(country, 'en')], ['Issuer', issuer], ['CoC Type', cocType], ['Issue Date', issueDate], diff --git a/apps/portal/src/app/features/waiver/pages/WaiverApplicationPage.tsx b/apps/portal/src/app/features/waiver/pages/WaiverApplicationPage.tsx index 02a3ba27c..74009b75f 100644 --- a/apps/portal/src/app/features/waiver/pages/WaiverApplicationPage.tsx +++ b/apps/portal/src/app/features/waiver/pages/WaiverApplicationPage.tsx @@ -266,7 +266,7 @@ export function WaiverApplicationPage() { businessLicenseNumber, applicantAddress, reasonNonEsl, bankName, bankBranch, letterRecipient, letterLanguage, productName, productDescription, quantity, unitOfMeasurement, - invoiceNumber, invoiceDate, invoiceAmount, countryOfOrigin: getCountryName(countryOfOrigin), supplierName, + invoiceNumber, invoiceDate, invoiceAmount, countryOfOrigin: getCountryName(countryOfOrigin, 'en'), supplierName, portOfLoading, portOfDischarge, fromPortName, toPortName, vesselName, carrierName, billOfLadingNumber, estimatedArrivalDate, actualArrivalDate, }, diff --git a/apps/portal/src/app/i18n/locales/am.ts b/apps/portal/src/app/i18n/locales/am.ts index f7139c2cd..a23b420d6 100644 --- a/apps/portal/src/app/i18n/locales/am.ts +++ b/apps/portal/src/app/i18n/locales/am.ts @@ -170,6 +170,11 @@ export const am: Translations = { }, }, + country: { + select: 'አገር ይምረጡ', + notFound: 'ምንም አገር አልተገኘም', + }, + location: { select: 'ይምረጡ...', noOptions: 'ምንም አማራጮች አልተገኙም', diff --git a/apps/portal/src/app/i18n/locales/en.ts b/apps/portal/src/app/i18n/locales/en.ts index dbae01a0f..9ddbc3079 100644 --- a/apps/portal/src/app/i18n/locales/en.ts +++ b/apps/portal/src/app/i18n/locales/en.ts @@ -168,6 +168,11 @@ export const en = { }, }, + country: { + select: 'Select a country', + notFound: 'No countries found', + }, + location: { select: 'Select...', noOptions: 'No options found', diff --git a/libs/ui/src/lib/input/CountrySelect.tsx b/libs/ui/src/lib/input/CountrySelect.tsx index f7392ee9e..73c300b5a 100644 --- a/libs/ui/src/lib/input/CountrySelect.tsx +++ b/libs/ui/src/lib/input/CountrySelect.tsx @@ -1,65 +1,24 @@ +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; import { Group, Select, Text, type ComboboxItem, type SelectProps } from '@mantine/core'; -import { getData, getName, overwrite } from 'country-list'; +import { registerLocale, getNames, getName } from 'i18n-iso-countries'; import * as Flags from 'country-flag-icons/react/3x2'; +import en from 'i18n-iso-countries/langs/en.json'; +import am from 'i18n-iso-countries/langs/am.json'; -// country-list ships raw ISO 3166 register names, which invert qualifiers into -// a trailing "(the)" / "(Qualifier of)" suffix — e.g. "Iran (Islamic Republic of)". -// Reorder every such entry into natural reading order ("Islamic Republic of Iran"). -// Full list + codes still come from country-list; this only rewrites display text. -overwrite([ - { code: 'AE', name: 'United Arab Emirates' }, - { code: 'BO', name: 'Plurinational State of Bolivia' }, - { code: 'BS', name: 'Bahamas' }, - { code: 'CC', name: 'Cocos (Keeling) Islands' }, - { code: 'CD', name: 'Democratic Republic of the Congo' }, - { code: 'CF', name: 'Central African Republic' }, - { code: 'CG', name: 'Congo' }, - { code: 'CK', name: 'Cook Islands' }, - { code: 'DO', name: 'Dominican Republic' }, - { code: 'FK', name: 'Falkland Islands (Malvinas)' }, - { code: 'FM', name: 'Federated States of Micronesia' }, - { code: 'FO', name: 'Faroe Islands' }, - { code: 'GB', name: 'United Kingdom of Great Britain and Northern Ireland' }, - { code: 'GM', name: 'Gambia' }, - { code: 'IO', name: 'British Indian Ocean Territory' }, - { code: 'IR', name: 'Islamic Republic of Iran' }, - { code: 'KM', name: 'Comoros' }, - { code: 'KP', name: "Democratic People's Republic of Korea" }, - { code: 'KR', name: 'Republic of Korea' }, - { code: 'KY', name: 'Cayman Islands' }, - { code: 'LA', name: "Lao People's Democratic Republic" }, - { code: 'MD', name: 'Republic of Moldova' }, - { code: 'MF', name: 'Saint Martin' }, - { code: 'MH', name: 'Marshall Islands' }, - { code: 'MP', name: 'Northern Mariana Islands' }, - { code: 'NE', name: 'Niger' }, - { code: 'NL', name: 'Kingdom of the Netherlands' }, - { code: 'PH', name: 'Philippines' }, - { code: 'PS', name: 'State of Palestine' }, - { code: 'RU', name: 'Russian Federation' }, - { code: 'SD', name: 'Sudan' }, - { code: 'SX', name: 'Sint Maarten' }, - { code: 'SY', name: 'Syrian Arab Republic' }, - { code: 'TC', name: 'Turks and Caicos Islands' }, - { code: 'TF', name: 'French Southern Territories' }, - { code: 'TW', name: 'Taiwan' }, - { code: 'TZ', name: 'United Republic of Tanzania' }, - { code: 'UM', name: 'United States Minor Outlying Islands' }, - { code: 'US', name: 'United States of America' }, - { code: 'VA', name: 'Holy See' }, - { code: 'VE', name: 'Bolivarian Republic of Venezuela' }, - { code: 'VG', name: 'British Virgin Islands' }, - { code: 'VI', name: 'United States Virgin Islands' }, -]); +// Registered once at module load — locale data is static. +registerLocale(en); +registerLocale(am); -// Built once at module load — static data, no hook memoization needed. -const COUNTRIES = getData() - .map(({ code, name }) => ({ value: code, label: name })) - .sort((a, b) => a.label.localeCompare(b.label)); +type CountryLang = 'en' | 'am'; -/** English country name for an alpha-2 code; '' when unset/unknown. */ -export function getCountryName(code: string | null | undefined): string { - return code ? (getName(code) ?? '') : ''; +function resolveLang(lng: string): CountryLang { + return lng === 'am' ? 'am' : 'en'; +} + +/** Localized country name for an alpha-2 code; '' when unset/unknown. */ +export function getCountryName(code: string | null | undefined, lang: CountryLang = 'en'): string { + return code ? (getName(code, lang) ?? '') : ''; } function CountryFlag({ code }: { code: string }) { @@ -98,19 +57,30 @@ export interface CountrySelectProps { export function CountrySelect({ value, onChange, - placeholder = 'Select a country', + placeholder, ...rest }: CountrySelectProps) { + const { t, i18n } = useTranslation(); + const lang = resolveLang(i18n.language); + + const countries = useMemo( + () => + Object.entries(getNames(lang)) + .map(([code, label]) => ({ value: code, label })) + .sort((a, b) => a.label.localeCompare(b.label, lang)), + [lang], + ); + return (