Skip to content
Merged
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
149 changes: 45 additions & 104 deletions scripts/normalize-xlf.prebuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,43 @@ const TEST_LANGUAGES = [
{ code: 'rl', target: 'RL' },
]

// Path to the standalone print-view script. Its $localize strings are NOT
// picked up by `ng extract-i18n` (the file is a plain asset, not part of the
// Angular compilation), so we parse them out here and inject them into the
// XLF. Parsing the file directly keeps fetch-orcid.js as the single source of
// truth: strings added/removed there propagate automatically, with no
// hardcoded list to drift out of sync.
const PRINT_VIEW_SOURCE_FILE = './src/assets/print-view/fetch-orcid.js'

interface PrintViewUnit {
id: string
source: string
}

function parsePrintViewUnits(path: string): PrintViewUnit[] {
const js = fs.readFileSync(path, 'utf8')
// Matches $localize tagged templates of the form `:@@printView.someId:Source text`
const re = /:@@(printView\.[A-Za-z0-9_]+):([^`]*)`/g
const units: PrintViewUnit[] = []
const seen = new Set<string>()
let match: RegExpExecArray | null
while ((match = re.exec(js)) !== null) {
const [, id, rawSource] = match
if (seen.has(id)) {
continue
}
seen.add(id)
// Convert $localize template placeholders `${expression}:NAME:` into the
// canonical $localize message form `{$NAME}` so the XLF source matches the
// runtime message and stays stable regardless of the JS expression.
const source = normalizeText(
rawSource.replace(/\$\{[^}]*\}:([A-Z0-9_]+):/g, '{$$$1}')
)
units.push({ id, source })
}
return units
}

function normalizeText(input: string): string {
return input
.replace(/\r/g, '')
Expand Down Expand Up @@ -85,115 +122,19 @@ function normalizeXlf12Sources(path: string) {
}
})

// Inject @@printView.* source-only trans-units so the test-language
// generator (generateTestingLanguages) will also stamp X / LR / RL for
// these strings, and so Transifex can discover them for real locales.
// We use a stable @@-prefixed id so $localize can match by explicit id.
const PRINT_VIEW_UNITS = [
'printView.unnamedProfile',
'printView.orcidIdAlt',
'printView.biography',
'printView.personalInformation',
'printView.emails',
'printView.websitesSocialLinks',
'printView.otherIds',
'printView.keywords',
'printView.countries',
'printView.activities',
'printView.employments',
'printView.educationAndQualifications',
'printView.professionalActivities',
'printView.fundings',
'printView.researchResources',
'printView.works',
'printView.organization',
'printView.organizationAddress',
'printView.startDate',
'printView.endDate',
'printView.publicationDate',
'printView.journal',
'printView.roleTitle',
'printView.department',
'printView.type',
'printView.url',
'printView.untitled',
'printView.identifier',
'printView.enterOrcidId',
'printView.orcidIdHelp',
'printView.loadProfile',
'printView.invalidOrcidId',
'printView.loadingRecord',
'printView.recordNotFound',
'printView.redirectingToPrimary',
'printView.fetchFailed',
'printView.couldNotLoad',
'printView.activityGroupHeading',
'printView.peerReviewHeading',
'printView.printSaveAsPdf',
'printView.printThisOrcidProfile',
'printView.relSelf',
'printView.relPartOf',
'printView.relVersionOf',
'printView.relFundedBy',
]
const PRINT_VIEW_SOURCES: Record<string, string> = {
'printView.unnamedProfile': 'Unnamed ORCID profile',
'printView.orcidIdAlt': 'ORCID iD',
'printView.biography': 'Biography',
'printView.personalInformation': 'Personal information',
'printView.emails': 'Emails',
'printView.websitesSocialLinks': 'Websites & social links',
'printView.otherIds': 'Other IDs',
'printView.keywords': 'Keywords',
'printView.countries': 'Countries',
'printView.activities': 'Activities',
'printView.employments': 'Employments',
'printView.educationAndQualifications': 'Education and qualifications',
'printView.professionalActivities': 'Professional activities',
'printView.fundings': 'Fundings',
'printView.researchResources': 'Research Resources',
'printView.works': 'Works',
'printView.organization': 'Organization',
'printView.organizationAddress': 'Organization address',
'printView.startDate': 'Start date',
'printView.endDate': 'End date',
'printView.publicationDate': 'Publication date',
'printView.journal': 'Journal',
'printView.roleTitle': 'Role title',
'printView.department': 'Department',
'printView.type': 'Type',
'printView.url': 'URL',
'printView.untitled': 'Untitled',
'printView.identifier': 'Identifier',
'printView.enterOrcidId': 'Enter an ORCID iD',
'printView.orcidIdHelp':
'Add an ORCID iD to the URL or use the form below.',
'printView.loadProfile': 'Load profile',
'printView.invalidOrcidId':
'Enter a valid ORCID iD (format: 0000-0000-0000-0000).',
'printView.loadingRecord': 'Loading ORCID record...',
'printView.recordNotFound':
'Record data was not found in ORCID response.',
'printView.redirectingToPrimary':
'Redirecting to primary ORCID record\u2026',
'printView.fetchFailed': 'Failed to fetch ORCID record',
'printView.couldNotLoad': 'Could not load',
'printView.activityGroupHeading': 'Activity group heading',
'printView.peerReviewHeading': 'Peer review heading',
'printView.printSaveAsPdf': 'Print / Save as PDF',
'printView.printThisOrcidProfile': 'Print this ORCID profile',
'printView.relSelf': 'Self',
'printView.relPartOf': 'Part of',
'printView.relVersionOf': 'Version of',
'printView.relFundedBy': 'Funded by',
}
// Inject @@printView.* source-only trans-units parsed from the standalone
// print-view script so the test-language generator (generateTestingLanguages)
// will also stamp X / LR / RL for these strings, and so Transifex can
// discover them for real locales. We use a stable @@-prefixed id so
// $localize can match by explicit id.
const printViewUnits = parsePrintViewUnits(PRINT_VIEW_SOURCE_FILE)
const existingIds = new Set(transUnits.map((tu: any) => tu?.$?.id))
const printViewBody = fileNode?.body?.[0]
PRINT_VIEW_UNITS.forEach((id) => {
printViewUnits.forEach(({ id, source }) => {
if (!existingIds.has(id)) {
printViewBody['trans-unit'].push({
$: { id, datatype: 'html', resname: id },
source: [PRINT_VIEW_SOURCES[id] ?? id],
source: [source],
})
}
})
Expand Down
2 changes: 1 addition & 1 deletion src/app/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export const ApplicationRoutesLabels = {
[ApplicationRoutes.resetPasswordEmail]: $localize`:@@share.resetPasswordEmail:Reset password - ORCID`,
[ApplicationRoutes.selfService]: $localize`:@@share.selfService:Self Service - ORCID`,
[ApplicationRoutes.developerTools]: $localize`:@@share.developerTools:Developer tools - ORCID`,
[ApplicationRoutes.smsPoc]: $localize`:@@share.smsPoc:SMS POC - ORCID`,
[ApplicationRoutes.smsPoc]: 'SMS POC - ORCID',
}

export const ApplicationDynamicRoutesLabels = {
Expand Down
28 changes: 10 additions & 18 deletions src/app/sms-poc/pages/sms-poc/sms-poc.component.html
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
<mat-card class="sms-poc-card max-w-[560px] sm:p-10! p-6!">
<h1 class="orc-font-heading-small font-normal mb-4" i18n="@@smsPoc.title">
<h1 class="orc-font-heading-small font-normal mb-4">
SMS POC
</h1>
<p
class="sms-poc-description orc-font-body-small mb-4"
i18n="@@smsPoc.description"
>
<p class="sms-poc-description orc-font-body-small mb-4">
Send a test SMS. Choose AWS or Twilio for each send.
</p>

<app-alert-message type="warning" role="note" class="mb-6 block">
<div content>
<span i18n="@@smsPoc.sandboxNotice">
<span>
This POC currently runs in SANDBOX mode. If you want to test actual SMS
delivery to your phone, please reach out to Leo on Slack to add your
phone number to our AWS SNS ORCID Friend Sandbox and the Twilio
Expand All @@ -22,21 +19,18 @@ <h1 class="orc-font-heading-small font-normal mb-4" i18n="@@smsPoc.title">

<form [formGroup]="smsForm" (ngSubmit)="onSubmit()" class="grid gap-4">
<div class="provider-selection">
<span
class="orc-font-small-print leading-4.5 font-bold! block mb-2"
i18n="@@smsPoc.provider"
>
<span class="orc-font-small-print leading-4.5 font-bold! block mb-2">
Provider
</span>
<mat-radio-group
formControlName="provider"
color="primary"
class="provider-group flex gap-4"
>
<mat-radio-button value="aws" i18n="@@smsPoc.providerAws">
<mat-radio-button value="aws">
AWS SNS
</mat-radio-button>
<mat-radio-button value="twilio" i18n="@@smsPoc.providerTwilio">
<mat-radio-button value="twilio">
Twilio
</mat-radio-button>
</mat-radio-group>
Expand All @@ -46,7 +40,6 @@ <h1 class="orc-font-heading-small font-normal mb-4" i18n="@@smsPoc.title">
<label
class="orc-font-small-print leading-4.5 font-bold! block mb-2"
for="sms-poc-phone"
i18n="@@smsPoc.phoneNumber"
>
Phone number
</label>
Expand All @@ -66,7 +59,7 @@ <h1 class="orc-font-heading-small font-normal mb-4" i18n="@@smsPoc.title">
</div>

<mat-form-field appearance="outline" [hideRequiredMarker]="true">
<mat-label i18n="@@smsPoc.message">Message</mat-label>
<mat-label>Message</mat-label>
<textarea
matInput
formControlName="message"
Expand All @@ -75,9 +68,9 @@ <h1 class="orc-font-heading-small font-normal mb-4" i18n="@@smsPoc.title">
></textarea>
<mat-hint align="end">{{ message?.value?.length || 0 }}/1600</mat-hint>
@if (message?.hasError('required') && message?.touched) {
<mat-error i18n="@@smsPoc.messageRequired">Message is required</mat-error>
<mat-error>Message is required</mat-error>
} @if (message?.hasError('maxlength') && message?.touched) {
<mat-error i18n="@@smsPoc.messageTooLong">
<mat-error>
Message must be 1600 characters or fewer
</mat-error>
}
Expand All @@ -90,7 +83,7 @@ <h1 class="orc-font-heading-small font-normal mb-4" i18n="@@smsPoc.title">
} @if (response?.success) {
<app-alert-message type="success" role="status" class="mb-2">
<div content>
<span i18n="@@smsPoc.sent">SMS sent.</span>
<span>SMS sent.</span>
<span>
{{ response.provider }}:
{{ response.providerMessageId || response.status }}
Expand All @@ -107,7 +100,6 @@ <h1 class="orc-font-heading-small font-normal mb-4" i18n="@@smsPoc.title">
type="submit"
[disabled]="loading"
[ngClass]="{ 'button-loading': loading }"
i18n="@@smsPoc.send"
>
Send SMS
</button>
Expand Down
6 changes: 3 additions & 3 deletions src/app/sms-poc/pages/sms-poc/sms-poc.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ export class SmsPocComponent implements OnInit {
return null
}
if (this.phoneNumber.hasError('required')) {
return $localize`:@@smsPoc.phoneRequired:Phone number is required`
return 'Phone number is required'
}
if (this.phoneNumber.hasError('invalidPhone')) {
return $localize`:@@smsPoc.phoneInvalid:Enter a valid phone number`
return 'Enter a valid phone number'
}
return null
}
Expand All @@ -73,7 +73,7 @@ export class SmsPocComponent implements OnInit {
},
error: () => {
this.loading = false
this.backendError = $localize`:@@smsPoc.sendFailed:SMS send failed`
this.backendError = 'SMS send failed'
},
})
}
Expand Down
Loading
Loading