|
|
@@ -110,6 +110,64 @@ private extension NSStackView {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/// Tags profile form controls with English localization keys so labels and placeholders refresh when the user changes language.
|
|
|
+private enum ProfileFormLocalization {
|
|
|
+ static let labelPrefix = "profileForm.label."
|
|
|
+ static let sectionPrefix = "profileForm.section."
|
|
|
+ static let placeholderPrefix = "profileForm.placeholder."
|
|
|
+ static let buttonPrefix = "profileForm.button."
|
|
|
+
|
|
|
+ static func tagLabel(_ label: NSTextField, key: String) {
|
|
|
+ label.identifier = NSUserInterfaceItemIdentifier(labelPrefix + key)
|
|
|
+ }
|
|
|
+
|
|
|
+ static func tagSection(_ label: NSTextField, key: String) {
|
|
|
+ label.identifier = NSUserInterfaceItemIdentifier(sectionPrefix + key)
|
|
|
+ }
|
|
|
+
|
|
|
+ static func tagPlaceholder(_ field: NSTextField, key: String) {
|
|
|
+ field.identifier = NSUserInterfaceItemIdentifier(placeholderPrefix + key)
|
|
|
+ }
|
|
|
+
|
|
|
+ static func tagButton(_ button: NSButton, key: String) {
|
|
|
+ button.identifier = NSUserInterfaceItemIdentifier(buttonPrefix + key)
|
|
|
+ }
|
|
|
+
|
|
|
+ static func placeholderAttributes() -> [NSAttributedString.Key: Any] {
|
|
|
+ [
|
|
|
+ .foregroundColor: ProfilePagePalette.secondaryText,
|
|
|
+ .font: NSFont.systemFont(ofSize: 14, weight: .regular),
|
|
|
+ .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+ static func applyPlaceholder(_ text: String, to field: NSTextField) {
|
|
|
+ field.placeholderAttributedString = NSAttributedString(string: text, attributes: placeholderAttributes())
|
|
|
+ }
|
|
|
+
|
|
|
+ static func refresh(in root: NSView) {
|
|
|
+ for view in root.profileSubviewsRecursive() {
|
|
|
+ guard let rawID = view.identifier?.rawValue else { continue }
|
|
|
+ if let label = view as? NSTextField, !label.isEditable {
|
|
|
+ if rawID.hasPrefix(labelPrefix) {
|
|
|
+ label.stringValue = L(String(rawID.dropFirst(labelPrefix.count)))
|
|
|
+ } else if rawID.hasPrefix(sectionPrefix) {
|
|
|
+ label.stringValue = L(String(rawID.dropFirst(sectionPrefix.count)))
|
|
|
+ }
|
|
|
+ } else if let field = view as? NSTextField, field.isEditable, rawID.hasPrefix(placeholderPrefix) {
|
|
|
+ applyPlaceholder(L(String(rawID.dropFirst(placeholderPrefix.count))), to: field)
|
|
|
+ } else if let button = view as? NSButton, rawID.hasPrefix(buttonPrefix) {
|
|
|
+ let key = String(rawID.dropFirst(buttonPrefix.count))
|
|
|
+ if button.image != nil {
|
|
|
+ button.image = NSImage(systemSymbolName: "trash", accessibilityDescription: L(key))
|
|
|
+ } else {
|
|
|
+ button.title = L(key)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/// Two fields side‑by‑side with a true 50/50 split, or stacked full‑width when compact. Avoids `NSStackView` collapsing paired columns to a narrow strip on the trailing edge.
|
|
|
private final class ProfileDualFieldRow: NSView {
|
|
|
private let leftView: NSView
|
|
|
@@ -282,6 +340,8 @@ final class MyProfilePageView: NSView {
|
|
|
backButton.title = L("← All profiles")
|
|
|
saveButton.title = L("Save Profile →")
|
|
|
contextLabel.stringValue = editingProfileID == nil ? L("New profile") : L("Edit profile")
|
|
|
+ ProfileFormLocalization.refresh(in: formStack)
|
|
|
+ referralHelperLabel?.stringValue = L("If someone referred you for this job, enter their name or company here")
|
|
|
renumberWorkExperienceEntries()
|
|
|
renumberEducationEntries()
|
|
|
ProfileThemeAppearance.refreshFormSubtree(formStack)
|
|
|
@@ -454,22 +514,22 @@ final class MyProfilePageView: NSView {
|
|
|
])
|
|
|
|
|
|
addFullWidthArrangedSubview(
|
|
|
- labeledGroup(title: L("Profile Name *"), field: profileNameField, placeholder: L("Marketing Director Profile"))
|
|
|
+ labeledGroup(labelKey: "Profile Name *", field: profileNameField, placeholderKey: "Marketing Director Profile")
|
|
|
)
|
|
|
- addFullWidthArrangedSubview(sectionHeading(L("Personal Information")))
|
|
|
+ addFullWidthArrangedSubview(sectionHeading("Personal Information"))
|
|
|
|
|
|
- let nameGroup = labeledGroup(title: L("Full Name *"), field: fullNameField, placeholder: L("John Doe"))
|
|
|
- let emailGroup = labeledGroup(title: L("Email *"), field: emailField, placeholder: L("john@example.com"))
|
|
|
+ let nameGroup = labeledGroup(labelKey: "Full Name *", field: fullNameField, placeholderKey: "John Doe")
|
|
|
+ let emailGroup = labeledGroup(labelKey: "Email *", field: emailField, placeholderKey: "john@example.com")
|
|
|
nameEmailRow = ProfileDualFieldRow(left: nameGroup, right: emailGroup, spacing: 12)
|
|
|
addFullWidthArrangedSubview(nameEmailRow)
|
|
|
|
|
|
- let phoneGroup = labeledGroup(title: L("Phone"), field: phoneField, placeholder: L("+1 (555) 123-4567"))
|
|
|
- let jobGroup = labeledGroup(title: L("Job Title *"), field: jobTitleField, placeholder: L("Software Engineer"))
|
|
|
+ let phoneGroup = labeledGroup(labelKey: "Phone", field: phoneField, placeholderKey: "+1 (555) 123-4567")
|
|
|
+ let jobGroup = labeledGroup(labelKey: "Job Title *", field: jobTitleField, placeholderKey: "Software Engineer")
|
|
|
phoneJobRow = ProfileDualFieldRow(left: phoneGroup, right: jobGroup, spacing: 12)
|
|
|
addFullWidthArrangedSubview(phoneJobRow)
|
|
|
|
|
|
addFullWidthArrangedSubview(
|
|
|
- labeledGroup(title: L("Address"), field: addressField, placeholder: L("123 Main St, City, State, ZIP"))
|
|
|
+ labeledGroup(labelKey: "Address", field: addressField, placeholderKey: "123 Main St, City, State, ZIP")
|
|
|
)
|
|
|
addFullWidthArrangedSubview(careerSummaryBlock())
|
|
|
addFullWidthArrangedSubview(horizontalSeparator())
|
|
|
@@ -479,8 +539,8 @@ final class MyProfilePageView: NSView {
|
|
|
addFullWidthArrangedSubview(horizontalSeparator())
|
|
|
addFullWidthArrangedSubview(
|
|
|
multilineProfileBlock(
|
|
|
- title: L("Certificates / Rewards"),
|
|
|
- placeholder: L("List your certificates and awards..."),
|
|
|
+ labelKey: "Certificates / Rewards",
|
|
|
+ placeholderKey: "List your certificates and awards...",
|
|
|
field: certificatesField,
|
|
|
minHeight: 100
|
|
|
)
|
|
|
@@ -488,8 +548,8 @@ final class MyProfilePageView: NSView {
|
|
|
addFullWidthArrangedSubview(horizontalSeparator())
|
|
|
addFullWidthArrangedSubview(
|
|
|
multilineProfileBlock(
|
|
|
- title: L("Interests"),
|
|
|
- placeholder: L("List your interests and hobbies..."),
|
|
|
+ labelKey: "Interests",
|
|
|
+ placeholderKey: "List your interests and hobbies...",
|
|
|
field: interestsField,
|
|
|
minHeight: 100
|
|
|
)
|
|
|
@@ -497,8 +557,8 @@ final class MyProfilePageView: NSView {
|
|
|
addFullWidthArrangedSubview(horizontalSeparator())
|
|
|
addFullWidthArrangedSubview(
|
|
|
multilineProfileBlock(
|
|
|
- title: L("Languages"),
|
|
|
- placeholder: L("List languages you speak (e.g., English - Native, Spanish - Fluent)..."),
|
|
|
+ labelKey: "Languages",
|
|
|
+ placeholderKey: "List languages you speak (e.g., English - Native, Spanish - Fluent)...",
|
|
|
field: languagesField,
|
|
|
minHeight: 100
|
|
|
)
|
|
|
@@ -518,6 +578,7 @@ final class MyProfilePageView: NSView {
|
|
|
|
|
|
func prepareNewProfile() {
|
|
|
editingProfileID = nil
|
|
|
+ applyLocalizedStrings()
|
|
|
contextLabel.stringValue = L("New profile")
|
|
|
applyForm(
|
|
|
from: SavedProfile(
|
|
|
@@ -537,6 +598,7 @@ final class MyProfilePageView: NSView {
|
|
|
|
|
|
func loadSavedProfile(_ profile: SavedProfile) {
|
|
|
editingProfileID = profile.id
|
|
|
+ applyLocalizedStrings()
|
|
|
contextLabel.stringValue = L("Edit profile")
|
|
|
applyForm(from: profile)
|
|
|
}
|
|
|
@@ -658,8 +720,9 @@ final class MyProfilePageView: NSView {
|
|
|
view.widthAnchor.constraint(equalTo: formStack.widthAnchor).isActive = true
|
|
|
}
|
|
|
|
|
|
- private func sectionHeading(_ text: String) -> NSView {
|
|
|
- let label = NSTextField(labelWithString: text)
|
|
|
+ private func sectionHeading(_ key: String) -> NSView {
|
|
|
+ let label = NSTextField(labelWithString: L(key))
|
|
|
+ ProfileFormLocalization.tagSection(label, key: key)
|
|
|
label.font = .systemFont(ofSize: 15, weight: .semibold)
|
|
|
label.textColor = ProfilePagePalette.primaryText
|
|
|
label.baseWritingDirection = .leftToRight
|
|
|
@@ -685,15 +748,16 @@ final class MyProfilePageView: NSView {
|
|
|
return row
|
|
|
}
|
|
|
|
|
|
- private func labeledGroup(title: String, field: NSTextField, placeholder: String) -> NSView {
|
|
|
- let label = NSTextField(labelWithString: title)
|
|
|
+ private func labeledGroup(labelKey: String, field: NSTextField, placeholderKey: String) -> NSView {
|
|
|
+ let label = NSTextField(labelWithString: L(labelKey))
|
|
|
+ ProfileFormLocalization.tagLabel(label, key: labelKey)
|
|
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
label.textColor = ProfilePagePalette.secondaryText
|
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
|
label.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
|
|
|
|
|
|
- styleSingleLineField(field, placeholder: placeholder)
|
|
|
+ styleSingleLineField(field, placeholderKey: placeholderKey)
|
|
|
let wrap = roundedFieldChrome(containing: field, minHeight: 40)
|
|
|
|
|
|
let stack = NSStackView(views: [label, wrap])
|
|
|
@@ -715,7 +779,8 @@ final class MyProfilePageView: NSView {
|
|
|
return stack
|
|
|
}
|
|
|
|
|
|
- private func styleSingleLineField(_ field: NSTextField, placeholder: String) {
|
|
|
+ private func styleSingleLineField(_ field: NSTextField, placeholderKey: String) {
|
|
|
+ ProfileFormLocalization.tagPlaceholder(field, key: placeholderKey)
|
|
|
field.translatesAutoresizingMaskIntoConstraints = false
|
|
|
field.isBordered = false
|
|
|
field.drawsBackground = false
|
|
|
@@ -724,15 +789,7 @@ final class MyProfilePageView: NSView {
|
|
|
field.textColor = ProfilePagePalette.primaryText
|
|
|
field.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
field.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
- let paragraph = ProfileLayoutEnforcement.leftAlignedParagraphStyle()
|
|
|
- field.placeholderAttributedString = NSAttributedString(
|
|
|
- string: placeholder,
|
|
|
- attributes: [
|
|
|
- .foregroundColor: ProfilePagePalette.secondaryText,
|
|
|
- .font: NSFont.systemFont(ofSize: 14, weight: .regular),
|
|
|
- .paragraphStyle: paragraph
|
|
|
- ]
|
|
|
- )
|
|
|
+ ProfileFormLocalization.applyPlaceholder(L(placeholderKey), to: field)
|
|
|
field.cell?.usesSingleLineMode = true
|
|
|
field.cell?.wraps = false
|
|
|
field.cell?.isScrollable = true
|
|
|
@@ -763,6 +820,7 @@ final class MyProfilePageView: NSView {
|
|
|
|
|
|
private func careerSummaryBlock() -> NSView {
|
|
|
let label = NSTextField(labelWithString: L("Career Summary"))
|
|
|
+ ProfileFormLocalization.tagLabel(label, key: "Career Summary")
|
|
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
label.textColor = ProfilePagePalette.secondaryText
|
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -783,13 +841,13 @@ final class MyProfilePageView: NSView {
|
|
|
careerField.stringValue = ""
|
|
|
careerField.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
careerField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
- careerField.placeholderAttributedString = NSAttributedString(
|
|
|
- string: L("Brief overview of your professional background and key achievements..."),
|
|
|
- attributes: [
|
|
|
- .foregroundColor: ProfilePagePalette.secondaryText,
|
|
|
- .font: NSFont.systemFont(ofSize: 14, weight: .regular),
|
|
|
- .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
|
|
|
- ]
|
|
|
+ ProfileFormLocalization.tagPlaceholder(
|
|
|
+ careerField,
|
|
|
+ key: "Brief overview of your professional background and key achievements..."
|
|
|
+ )
|
|
|
+ ProfileFormLocalization.applyPlaceholder(
|
|
|
+ L("Brief overview of your professional background and key achievements..."),
|
|
|
+ to: careerField
|
|
|
)
|
|
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(careerField)
|
|
|
|
|
|
@@ -830,13 +888,15 @@ final class MyProfilePageView: NSView {
|
|
|
return stack
|
|
|
}
|
|
|
|
|
|
- private func multilineProfileBlock(title: String, placeholder: String, field: NSTextField, minHeight: CGFloat) -> NSView {
|
|
|
- let label = NSTextField(labelWithString: title)
|
|
|
+ private func multilineProfileBlock(labelKey: String, placeholderKey: String, field: NSTextField, minHeight: CGFloat) -> NSView {
|
|
|
+ let label = NSTextField(labelWithString: L(labelKey))
|
|
|
+ ProfileFormLocalization.tagLabel(label, key: labelKey)
|
|
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
label.textColor = ProfilePagePalette.secondaryText
|
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
|
|
|
|
|
|
+ ProfileFormLocalization.tagPlaceholder(field, key: placeholderKey)
|
|
|
field.translatesAutoresizingMaskIntoConstraints = false
|
|
|
field.isEditable = true
|
|
|
field.isSelectable = true
|
|
|
@@ -852,14 +912,7 @@ final class MyProfilePageView: NSView {
|
|
|
field.stringValue = ""
|
|
|
field.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
field.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
- field.placeholderAttributedString = NSAttributedString(
|
|
|
- string: placeholder,
|
|
|
- attributes: [
|
|
|
- .foregroundColor: ProfilePagePalette.secondaryText,
|
|
|
- .font: NSFont.systemFont(ofSize: 14, weight: .regular),
|
|
|
- .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
|
|
|
- ]
|
|
|
- )
|
|
|
+ ProfileFormLocalization.applyPlaceholder(L(placeholderKey), to: field)
|
|
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(field)
|
|
|
|
|
|
let wrap = NSView()
|
|
|
@@ -901,12 +954,13 @@ final class MyProfilePageView: NSView {
|
|
|
|
|
|
private func referralBlock() -> NSView {
|
|
|
let label = NSTextField(labelWithString: L("Referral (Optional)"))
|
|
|
+ ProfileFormLocalization.tagLabel(label, key: "Referral (Optional)")
|
|
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
label.textColor = ProfilePagePalette.secondaryText
|
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
|
|
|
|
|
|
- styleSingleLineField(referralField, placeholder: L("Referred by (Company/Person Name)"))
|
|
|
+ styleSingleLineField(referralField, placeholderKey: "Referred by (Company/Person Name)")
|
|
|
let wrap = roundedFieldChrome(containing: referralField, minHeight: 40)
|
|
|
|
|
|
let helper = NSTextField(wrappingLabelWithString: L("If someone referred you for this job, enter their name or company here"))
|
|
|
@@ -947,6 +1001,7 @@ final class MyProfilePageView: NSView {
|
|
|
|
|
|
private func workExperienceSection() -> NSView {
|
|
|
let title = NSTextField(labelWithString: L("Work Experience"))
|
|
|
+ ProfileFormLocalization.tagSection(title, key: "Work Experience")
|
|
|
title.font = .systemFont(ofSize: 15, weight: .semibold)
|
|
|
title.textColor = ProfilePagePalette.primaryText
|
|
|
title.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -954,6 +1009,7 @@ final class MyProfilePageView: NSView {
|
|
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(title)
|
|
|
|
|
|
let addButton = NSButton(title: L("+ Add Another"), target: self, action: #selector(didTapAddWorkExperience))
|
|
|
+ ProfileFormLocalization.tagButton(addButton, key: "+ Add Another")
|
|
|
addButton.translatesAutoresizingMaskIntoConstraints = false
|
|
|
addButton.bezelStyle = .rounded
|
|
|
addButton.isBordered = true
|
|
|
@@ -994,6 +1050,7 @@ final class MyProfilePageView: NSView {
|
|
|
|
|
|
private func educationSection() -> NSView {
|
|
|
let title = NSTextField(labelWithString: L("Education"))
|
|
|
+ ProfileFormLocalization.tagSection(title, key: "Education")
|
|
|
title.font = .systemFont(ofSize: 15, weight: .semibold)
|
|
|
title.textColor = ProfilePagePalette.primaryText
|
|
|
title.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -1001,6 +1058,7 @@ final class MyProfilePageView: NSView {
|
|
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(title)
|
|
|
|
|
|
let addButton = NSButton(title: L("+ Add Another"), target: self, action: #selector(didTapAddEducation))
|
|
|
+ ProfileFormLocalization.tagButton(addButton, key: "+ Add Another")
|
|
|
addButton.translatesAutoresizingMaskIntoConstraints = false
|
|
|
addButton.bezelStyle = .rounded
|
|
|
addButton.isBordered = true
|
|
|
@@ -1259,9 +1317,11 @@ private final class WorkExperienceEntryView: NSView {
|
|
|
if #available(macOS 11.0, *) {
|
|
|
deleteButton.image = NSImage(systemSymbolName: "trash", accessibilityDescription: L("Remove experience"))
|
|
|
deleteButton.imagePosition = .imageOnly
|
|
|
+ ProfileFormLocalization.tagButton(deleteButton, key: "Remove experience")
|
|
|
} else {
|
|
|
deleteButton.title = L("Remove")
|
|
|
deleteButton.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
+ ProfileFormLocalization.tagButton(deleteButton, key: "Remove")
|
|
|
}
|
|
|
|
|
|
let headerSpacer = NSView()
|
|
|
@@ -1276,15 +1336,27 @@ private final class WorkExperienceEntryView: NSView {
|
|
|
ProfileLayoutEnforcement.applyForcedLTR(to: headerRow)
|
|
|
headerRow.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
|
- let jobGroup = Self.labeledFieldStack(title: L("Job Title *"), field: jobTitleField, placeholder: L("e.g., Software Engineer"))
|
|
|
- let companyGroup = Self.labeledFieldStack(title: L("Company Name *"), field: companyField, placeholder: L("e.g., Google"))
|
|
|
+ let jobGroup = Self.labeledFieldStack(
|
|
|
+ labelKey: "Job Title *",
|
|
|
+ field: jobTitleField,
|
|
|
+ placeholderKey: "e.g., Software Engineer"
|
|
|
+ )
|
|
|
+ let companyGroup = Self.labeledFieldStack(
|
|
|
+ labelKey: "Company Name *",
|
|
|
+ field: companyField,
|
|
|
+ placeholderKey: "e.g., Google"
|
|
|
+ )
|
|
|
jobCompanyRow = ProfileDualFieldRow(left: jobGroup, right: companyGroup, spacing: 12)
|
|
|
|
|
|
- let durationGroup = Self.labeledFieldStack(title: L("Duration *"), field: durationField, placeholder: L("e.g., Jan 2020 - Present"))
|
|
|
+ let durationGroup = Self.labeledFieldStack(
|
|
|
+ labelKey: "Duration *",
|
|
|
+ field: durationField,
|
|
|
+ placeholderKey: "e.g., Jan 2020 - Present"
|
|
|
+ )
|
|
|
let descriptionGroup = Self.multilineLabeledStack(
|
|
|
- title: L("Description"),
|
|
|
+ labelKey: "Description",
|
|
|
field: descriptionField,
|
|
|
- placeholder: L("Describe your responsibilities and achievements..."),
|
|
|
+ placeholderKey: "Describe your responsibilities and achievements...",
|
|
|
minHeight: 120
|
|
|
)
|
|
|
|
|
|
@@ -1334,14 +1406,15 @@ private final class WorkExperienceEntryView: NSView {
|
|
|
ProfileThemeAppearance.refreshFormSubtree(self)
|
|
|
}
|
|
|
|
|
|
- fileprivate static func labeledFieldStack(title: String, field: NSTextField, placeholder: String) -> NSView {
|
|
|
- let label = NSTextField(labelWithString: title)
|
|
|
+ fileprivate static func labeledFieldStack(labelKey: String, field: NSTextField, placeholderKey: String) -> NSView {
|
|
|
+ let label = NSTextField(labelWithString: L(labelKey))
|
|
|
+ ProfileFormLocalization.tagLabel(label, key: labelKey)
|
|
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
label.textColor = ProfilePagePalette.secondaryText
|
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
|
|
|
|
|
|
- styleSingleLineField(field, placeholder: placeholder)
|
|
|
+ styleSingleLineField(field, placeholderKey: placeholderKey)
|
|
|
let wrap = roundedChrome(around: field, minHeight: 40)
|
|
|
|
|
|
let stack = NSStackView(views: [label, wrap])
|
|
|
@@ -1362,13 +1435,15 @@ private final class WorkExperienceEntryView: NSView {
|
|
|
return stack
|
|
|
}
|
|
|
|
|
|
- private static func multilineLabeledStack(title: String, field: NSTextField, placeholder: String, minHeight: CGFloat) -> NSView {
|
|
|
- let label = NSTextField(labelWithString: title)
|
|
|
+ private static func multilineLabeledStack(labelKey: String, field: NSTextField, placeholderKey: String, minHeight: CGFloat) -> NSView {
|
|
|
+ let label = NSTextField(labelWithString: L(labelKey))
|
|
|
+ ProfileFormLocalization.tagLabel(label, key: labelKey)
|
|
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
label.textColor = ProfilePagePalette.secondaryText
|
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
|
|
|
|
|
|
+ ProfileFormLocalization.tagPlaceholder(field, key: placeholderKey)
|
|
|
field.translatesAutoresizingMaskIntoConstraints = false
|
|
|
field.isEditable = true
|
|
|
field.isSelectable = true
|
|
|
@@ -1381,14 +1456,7 @@ private final class WorkExperienceEntryView: NSView {
|
|
|
field.cell?.wraps = true
|
|
|
field.cell?.isScrollable = false
|
|
|
field.cell?.usesSingleLineMode = false
|
|
|
- field.placeholderAttributedString = NSAttributedString(
|
|
|
- string: placeholder,
|
|
|
- attributes: [
|
|
|
- .foregroundColor: ProfilePagePalette.secondaryText,
|
|
|
- .font: NSFont.systemFont(ofSize: 14, weight: .regular),
|
|
|
- .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
|
|
|
- ]
|
|
|
- )
|
|
|
+ ProfileFormLocalization.applyPlaceholder(L(placeholderKey), to: field)
|
|
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(field)
|
|
|
|
|
|
let wrap = NSView()
|
|
|
@@ -1428,21 +1496,15 @@ private final class WorkExperienceEntryView: NSView {
|
|
|
return stack
|
|
|
}
|
|
|
|
|
|
- private static func styleSingleLineField(_ field: NSTextField, placeholder: String) {
|
|
|
+ private static func styleSingleLineField(_ field: NSTextField, placeholderKey: String) {
|
|
|
+ ProfileFormLocalization.tagPlaceholder(field, key: placeholderKey)
|
|
|
field.translatesAutoresizingMaskIntoConstraints = false
|
|
|
field.isBordered = false
|
|
|
field.drawsBackground = false
|
|
|
field.focusRingType = .none
|
|
|
field.font = .systemFont(ofSize: 14, weight: .regular)
|
|
|
field.textColor = ProfilePagePalette.primaryText
|
|
|
- field.placeholderAttributedString = NSAttributedString(
|
|
|
- string: placeholder,
|
|
|
- attributes: [
|
|
|
- .foregroundColor: ProfilePagePalette.secondaryText,
|
|
|
- .font: NSFont.systemFont(ofSize: 14, weight: .regular),
|
|
|
- .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
|
|
|
- ]
|
|
|
- )
|
|
|
+ ProfileFormLocalization.applyPlaceholder(L(placeholderKey), to: field)
|
|
|
field.cell?.usesSingleLineMode = true
|
|
|
field.cell?.wraps = false
|
|
|
field.cell?.isScrollable = true
|
|
|
@@ -1549,9 +1611,11 @@ private final class EducationEntryView: NSView {
|
|
|
if #available(macOS 11.0, *) {
|
|
|
deleteButton.image = NSImage(systemSymbolName: "trash", accessibilityDescription: L("Remove education"))
|
|
|
deleteButton.imagePosition = .imageOnly
|
|
|
+ ProfileFormLocalization.tagButton(deleteButton, key: "Remove education")
|
|
|
} else {
|
|
|
deleteButton.title = L("Remove")
|
|
|
deleteButton.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
+ ProfileFormLocalization.tagButton(deleteButton, key: "Remove")
|
|
|
}
|
|
|
|
|
|
let headerSpacer = NSView()
|
|
|
@@ -1567,21 +1631,21 @@ private final class EducationEntryView: NSView {
|
|
|
headerRow.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
|
let degreeGroup = WorkExperienceEntryView.labeledFieldStack(
|
|
|
- title: L("Degree / program *"),
|
|
|
+ labelKey: "Degree / program *",
|
|
|
field: degreeField,
|
|
|
- placeholder: L("e.g., BSc Computer Science")
|
|
|
+ placeholderKey: "e.g., BSc Computer Science"
|
|
|
)
|
|
|
let institutionGroup = WorkExperienceEntryView.labeledFieldStack(
|
|
|
- title: L("Institution *"),
|
|
|
+ labelKey: "Institution *",
|
|
|
field: institutionField,
|
|
|
- placeholder: L("e.g., MIT")
|
|
|
+ placeholderKey: "e.g., MIT"
|
|
|
)
|
|
|
degreeInstitutionRow = ProfileDualFieldRow(left: degreeGroup, right: institutionGroup, spacing: 12)
|
|
|
|
|
|
let yearGroup = WorkExperienceEntryView.labeledFieldStack(
|
|
|
- title: L("Year *"),
|
|
|
+ labelKey: "Year *",
|
|
|
field: yearField,
|
|
|
- placeholder: L("e.g., 2020")
|
|
|
+ placeholderKey: "e.g., 2020"
|
|
|
)
|
|
|
|
|
|
let inner = NSStackView(views: [headerRow, degreeInstitutionRow, yearGroup])
|