Skip to content

Commit b2f7723

Browse files
committed
PD-5670
1 parent 08a0746 commit b2f7723

10 files changed

Lines changed: 1091 additions & 34 deletions

SUPPORTED_BROWSERS.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
> This file is auto-generated from `.browserslistrc` during the prebuild step. Do not edit manually.
44
5-
| Browser | Minimum version |
6-
|---------|-----------------|
7-
| Android WebView | 145 or newer |
8-
| Apple Safari | 17.2 or newer |
9-
| Google Chrome | 120 or newer |
10-
| Microsoft Edge | 120 or newer |
11-
| Mozilla Firefox | 120 or newer |
12-
| Opera | 106 or newer |
13-
| Opera Mobile | 80 or newer |
14-
| Samsung Internet | 25 or newer |
5+
| Browser | Minimum version |
6+
| ---------------- | --------------- |
7+
| Android WebView | 145 or newer |
8+
| Apple Safari | 17.2 or newer |
9+
| Google Chrome | 120 or newer |
10+
| Microsoft Edge | 120 or newer |
11+
| Mozilla Firefox | 120 or newer |
12+
| Opera | 106 or newer |
13+
| Opera Mobile | 80 or newer |
14+
| Samsung Internet | 25 or newer |

angular.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,8 @@
426426
],
427427
"scripts": [
428428
"/scripts/environment.runtime.js",
429-
"/scripts/onetrust.runtime.js"
429+
"/scripts/onetrust.runtime.js",
430+
"src/assets/print-view/fetch-orcid.js"
430431
],
431432
"assets": ["src/favicon.ico", "src/assets", "src/manifest.json"],
432433
"sourceMap": true

scripts/normalize-xlf.prebuild.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,107 @@ function normalizeXlf12Sources(path: string) {
8585
}
8686
})
8787

88+
// Inject @@printView.* source-only trans-units so the test-language
89+
// generator (generateTestingLanguages) will also stamp X / LR / RL for
90+
// these strings, and so Transifex can discover them for real locales.
91+
// We use a stable @@-prefixed id so $localize can match by explicit id.
92+
const PRINT_VIEW_UNITS = [
93+
'printView.unnamedProfile',
94+
'printView.orcidIdAlt',
95+
'printView.biography',
96+
'printView.personalInformation',
97+
'printView.emails',
98+
'printView.websitesSocialLinks',
99+
'printView.otherIds',
100+
'printView.keywords',
101+
'printView.countries',
102+
'printView.activities',
103+
'printView.employments',
104+
'printView.educationAndQualifications',
105+
'printView.professionalActivities',
106+
'printView.fundings',
107+
'printView.researchResources',
108+
'printView.works',
109+
'printView.organization',
110+
'printView.organizationAddress',
111+
'printView.startDate',
112+
'printView.endDate',
113+
'printView.publicationDate',
114+
'printView.journal',
115+
'printView.roleTitle',
116+
'printView.department',
117+
'printView.type',
118+
'printView.url',
119+
'printView.untitled',
120+
'printView.identifier',
121+
'printView.enterOrcidId',
122+
'printView.orcidIdHelp',
123+
'printView.loadProfile',
124+
'printView.invalidOrcidId',
125+
'printView.loadingRecord',
126+
'printView.recordNotFound',
127+
'printView.redirectingToPrimary',
128+
'printView.fetchFailed',
129+
'printView.couldNotLoad',
130+
'printView.activityGroupHeading',
131+
'printView.peerReviewHeading',
132+
]
133+
const PRINT_VIEW_SOURCES: Record<string, string> = {
134+
'printView.unnamedProfile': 'Unnamed ORCID profile',
135+
'printView.orcidIdAlt': 'ORCID iD',
136+
'printView.biography': 'Biography',
137+
'printView.personalInformation': 'Personal information',
138+
'printView.emails': 'Emails',
139+
'printView.websitesSocialLinks': 'Websites & social links',
140+
'printView.otherIds': 'Other IDs',
141+
'printView.keywords': 'Keywords',
142+
'printView.countries': 'Countries',
143+
'printView.activities': 'Activities',
144+
'printView.employments': 'Employments',
145+
'printView.educationAndQualifications': 'Education and qualifications',
146+
'printView.professionalActivities': 'Professional activities',
147+
'printView.fundings': 'Fundings',
148+
'printView.researchResources': 'Research Resources',
149+
'printView.works': 'Works',
150+
'printView.organization': 'Organization',
151+
'printView.organizationAddress': 'Organization address',
152+
'printView.startDate': 'Start date',
153+
'printView.endDate': 'End date',
154+
'printView.publicationDate': 'Publication date',
155+
'printView.journal': 'Journal',
156+
'printView.roleTitle': 'Role title',
157+
'printView.department': 'Department',
158+
'printView.type': 'Type',
159+
'printView.url': 'URL',
160+
'printView.untitled': 'Untitled',
161+
'printView.identifier': 'Identifier',
162+
'printView.enterOrcidId': 'Enter an ORCID iD',
163+
'printView.orcidIdHelp':
164+
'Add an ORCID iD to the URL or use the form below.',
165+
'printView.loadProfile': 'Load profile',
166+
'printView.invalidOrcidId':
167+
'Enter a valid ORCID iD (format: 0000-0000-0000-0000).',
168+
'printView.loadingRecord': 'Loading ORCID record...',
169+
'printView.recordNotFound':
170+
'Record data was not found in ORCID response.',
171+
'printView.redirectingToPrimary':
172+
'Redirecting to primary ORCID record\u2026',
173+
'printView.fetchFailed': 'Failed to fetch ORCID record',
174+
'printView.couldNotLoad': 'Could not load',
175+
'printView.activityGroupHeading': 'Activity group heading',
176+
'printView.peerReviewHeading': 'Peer review heading',
177+
}
178+
const existingIds = new Set(transUnits.map((tu: any) => tu?.$?.id))
179+
const printViewBody = fileNode?.body?.[0]
180+
PRINT_VIEW_UNITS.forEach((id) => {
181+
if (!existingIds.has(id)) {
182+
printViewBody['trans-unit'].push({
183+
$: { id, datatype: 'html', resname: id },
184+
source: [PRINT_VIEW_SOURCES[id] ?? id],
185+
})
186+
}
187+
})
188+
88189
writeXlf(path, data)
89190
generateTestingLanguages(data)
90191
})

scripts/print-view-localize.postbuild.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,14 @@ function getTranslations(locale: string): Record<string, any> | null {
4848
}
4949
const content = readFileSync(xlf, 'utf8')
5050
const parser = new Xliff1TranslationParser()
51-
const analyzed = (parser as any).analyze(xlf, content) as { canParse: boolean; hint: any }
51+
const analyzed = (parser as any).analyze(xlf, content) as {
52+
canParse: boolean
53+
hint: any
54+
}
5255
if (!analyzed.canParse) {
53-
console.warn(`[print-view-localize] Could not parse XLF for locale "${locale}": ${xlf}`)
56+
console.warn(
57+
`[print-view-localize] Could not parse XLF for locale "${locale}": ${xlf}`
58+
)
5459
return null
5560
}
5661
const { translations } = parser.parse(xlf, content, analyzed.hint)
@@ -89,9 +94,13 @@ export function localizeAndWritePrintViewScript(): void {
8994
const result = babel.transformSync(source, {
9095
filename: PRINT_VIEW_SOURCE,
9196
plugins: [
92-
makeEs2015TranslatePlugin(diagnostics, {}, {
93-
missingTranslation: 'ignore',
94-
}),
97+
makeEs2015TranslatePlugin(
98+
diagnostics,
99+
{},
100+
{
101+
missingTranslation: 'ignore',
102+
}
103+
),
95104
],
96105
configFile: false,
97106
babelrc: false,
@@ -113,7 +122,7 @@ export function localizeAndWritePrintViewScript(): void {
113122
if (diagnostics.hasErrors) {
114123
console.warn(
115124
`[print-view-localize] Errors for locale "${locale}":`,
116-
diagnostics.messages,
125+
diagnostics.messages
117126
)
118127
}
119128

@@ -122,5 +131,22 @@ export function localizeAndWritePrintViewScript(): void {
122131

123132
writeFileSync(destFile, output, 'utf8')
124133
console.log(`[print-view-localize] ✓ ${destFile}`)
134+
135+
// Fix the lang attribute in the copied print-view/index.html.
136+
// The source file always has lang="en"; we rewrite it to the actual locale.
137+
const printViewIndexPath = `${destDir}/index.html`
138+
if (existsSync(printViewIndexPath)) {
139+
const htmlSource = readFileSync(printViewIndexPath, 'utf8')
140+
const htmlLocalized = htmlSource.replace(
141+
/<html([^>]*)lang="[^"]*"/,
142+
`<html$1lang="${locale}"`
143+
)
144+
if (htmlLocalized !== htmlSource) {
145+
writeFileSync(printViewIndexPath, htmlLocalized, 'utf8')
146+
console.log(
147+
`[print-view-localize] ✓ ${printViewIndexPath} (lang=${locale})`
148+
)
149+
}
150+
}
125151
}
126152
}

src/assets/print-view/fetch-orcid.js

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ if (typeof $localize === 'undefined') {
44
self.$localize = function (messageParts) {
55
var substitutions = Array.prototype.slice.call(arguments, 1)
66
return messageParts.reduce(function (result, part, i) {
7-
return result + (substitutions[i - 1] != null ? substitutions[i - 1] : '') + part
7+
return (
8+
result +
9+
(substitutions[i - 1] != null ? substitutions[i - 1] : '') +
10+
part
11+
)
812
})
913
}
1014
}
@@ -477,7 +481,7 @@ function renderActivityGroupFromJson(section, title, items, renderItem) {
477481
const block = document.createElement('div')
478482
block.className = 'activity-group'
479483
const heading = document.createElement('h3')
480-
heading.textContent = $localize`:@@printView.activityGroupHeading:${ title }:TITLE: (${ entries.length }:COUNT:)`
484+
heading.textContent = $localize`:@@printView.activityGroupHeading:${title}:TITLE: (${entries.length}:COUNT:)`
481485
block.appendChild(heading)
482486
const list = document.createElement('ul')
483487
entries.forEach((entry) => {
@@ -577,10 +581,14 @@ function renderEmployments(activities, section) {
577581

578582
hasContent = false
579583
hasContent =
580-
renderActivityGroupFromJson(section, STRINGS.employments, employments, (e) =>
581-
composeActivityEntryFromJson(e, {
582-
title: jsonText(e?.organization?.name) || jsonText(e?.roleTitle),
583-
})
584+
renderActivityGroupFromJson(
585+
section,
586+
STRINGS.employments,
587+
employments,
588+
(e) =>
589+
composeActivityEntryFromJson(e, {
590+
title: jsonText(e?.organization?.name) || jsonText(e?.roleTitle),
591+
})
584592
) || hasContent
585593
return hasContent
586594
}
@@ -821,7 +829,7 @@ function renderPeerReviews(activities, section) {
821829
const block = document.createElement('div')
822830
block.className = 'activity-group'
823831
const heading = document.createElement('h3')
824-
heading.textContent = $localize`:@@printView.peerReviewHeading:Peer review (${ reviews }:REVIEW_COUNT: reviews for ${ sortedPublications.size }:PUBLICATION_COUNT: publications/grants)`
832+
heading.textContent = $localize`:@@printView.peerReviewHeading:Peer review (${reviews}:REVIEW_COUNT: reviews for ${sortedPublications.size}:PUBLICATION_COUNT: publications/grants)`
825833
block.appendChild(heading)
826834
const list = document.createElement('ul')
827835
for (publication of sortedPublications || []) {
@@ -896,7 +904,9 @@ async function fetchOrcidRecord(orcidId) {
896904
}
897905
}
898906
if (!response.ok) {
899-
throw new Error($localize`:@@printView.fetchFailed:Failed to fetch ORCID record (${ response.status }:STATUS:).`)
907+
throw new Error(
908+
$localize`:@@printView.fetchFailed:Failed to fetch ORCID record (${response.status}:STATUS:).`
909+
)
900910
}
901911

902912
const recordJson = await response.json()
@@ -923,18 +933,26 @@ async function loadRecord(orcidId) {
923933
message.className = 'error'
924934
message.textContent = error.message
925935
cvRoot.appendChild(message)
926-
showStatus($localize`:@@printView.couldNotLoad:Could not load ${ orcidId }:ORCID_ID:.`, 'error')
936+
showStatus(
937+
$localize`:@@printView.couldNotLoad:Could not load ${orcidId}:ORCID_ID:.`,
938+
'error'
939+
)
927940
cvRoot.setAttribute('aria-busy', 'false')
928941
}
929942
}
930943

931-
printButton.addEventListener('click', () => window.print())
944+
// Guard: only run DOM initialization when the required elements are present.
945+
// This allows the file to be loaded in test environments (Karma/Jasmine) where
946+
// the print-view HTML is not present and getElementById returns null.
947+
if (cvRoot && statusNode && printButton) {
948+
printButton.addEventListener('click', () => window.print())
932949

933-
const startingId = resolveOrcidIdFromLocation()
934-
if (startingId) {
935-
syncOrcidInUrl(startingId)
936-
loadRecord(startingId)
937-
} else {
938-
clearStatus()
939-
renderOrcidPrompt()
950+
const startingId = resolveOrcidIdFromLocation()
951+
if (startingId) {
952+
syncOrcidInUrl(startingId)
953+
loadRecord(startingId)
954+
} else {
955+
clearStatus()
956+
renderOrcidPrompt()
957+
}
940958
}

0 commit comments

Comments
 (0)