|
|
@@ -110,6 +110,64 @@ private extension NSStackView {
|
|
110
|
110
|
}
|
|
111
|
111
|
}
|
|
112
|
112
|
|
|
|
113
|
+/// Tags profile form controls with English localization keys so labels and placeholders refresh when the user changes language.
|
|
|
114
|
+private enum ProfileFormLocalization {
|
|
|
115
|
+ static let labelPrefix = "profileForm.label."
|
|
|
116
|
+ static let sectionPrefix = "profileForm.section."
|
|
|
117
|
+ static let placeholderPrefix = "profileForm.placeholder."
|
|
|
118
|
+ static let buttonPrefix = "profileForm.button."
|
|
|
119
|
+
|
|
|
120
|
+ static func tagLabel(_ label: NSTextField, key: String) {
|
|
|
121
|
+ label.identifier = NSUserInterfaceItemIdentifier(labelPrefix + key)
|
|
|
122
|
+ }
|
|
|
123
|
+
|
|
|
124
|
+ static func tagSection(_ label: NSTextField, key: String) {
|
|
|
125
|
+ label.identifier = NSUserInterfaceItemIdentifier(sectionPrefix + key)
|
|
|
126
|
+ }
|
|
|
127
|
+
|
|
|
128
|
+ static func tagPlaceholder(_ field: NSTextField, key: String) {
|
|
|
129
|
+ field.identifier = NSUserInterfaceItemIdentifier(placeholderPrefix + key)
|
|
|
130
|
+ }
|
|
|
131
|
+
|
|
|
132
|
+ static func tagButton(_ button: NSButton, key: String) {
|
|
|
133
|
+ button.identifier = NSUserInterfaceItemIdentifier(buttonPrefix + key)
|
|
|
134
|
+ }
|
|
|
135
|
+
|
|
|
136
|
+ static func placeholderAttributes() -> [NSAttributedString.Key: Any] {
|
|
|
137
|
+ [
|
|
|
138
|
+ .foregroundColor: ProfilePagePalette.secondaryText,
|
|
|
139
|
+ .font: NSFont.systemFont(ofSize: 14, weight: .regular),
|
|
|
140
|
+ .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
|
|
|
141
|
+ ]
|
|
|
142
|
+ }
|
|
|
143
|
+
|
|
|
144
|
+ static func applyPlaceholder(_ text: String, to field: NSTextField) {
|
|
|
145
|
+ field.placeholderAttributedString = NSAttributedString(string: text, attributes: placeholderAttributes())
|
|
|
146
|
+ }
|
|
|
147
|
+
|
|
|
148
|
+ static func refresh(in root: NSView) {
|
|
|
149
|
+ for view in root.profileSubviewsRecursive() {
|
|
|
150
|
+ guard let rawID = view.identifier?.rawValue else { continue }
|
|
|
151
|
+ if let label = view as? NSTextField, !label.isEditable {
|
|
|
152
|
+ if rawID.hasPrefix(labelPrefix) {
|
|
|
153
|
+ label.stringValue = L(String(rawID.dropFirst(labelPrefix.count)))
|
|
|
154
|
+ } else if rawID.hasPrefix(sectionPrefix) {
|
|
|
155
|
+ label.stringValue = L(String(rawID.dropFirst(sectionPrefix.count)))
|
|
|
156
|
+ }
|
|
|
157
|
+ } else if let field = view as? NSTextField, field.isEditable, rawID.hasPrefix(placeholderPrefix) {
|
|
|
158
|
+ applyPlaceholder(L(String(rawID.dropFirst(placeholderPrefix.count))), to: field)
|
|
|
159
|
+ } else if let button = view as? NSButton, rawID.hasPrefix(buttonPrefix) {
|
|
|
160
|
+ let key = String(rawID.dropFirst(buttonPrefix.count))
|
|
|
161
|
+ if button.image != nil {
|
|
|
162
|
+ button.image = NSImage(systemSymbolName: "trash", accessibilityDescription: L(key))
|
|
|
163
|
+ } else {
|
|
|
164
|
+ button.title = L(key)
|
|
|
165
|
+ }
|
|
|
166
|
+ }
|
|
|
167
|
+ }
|
|
|
168
|
+ }
|
|
|
169
|
+}
|
|
|
170
|
+
|
|
113
|
171
|
/// 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.
|
|
114
|
172
|
private final class ProfileDualFieldRow: NSView {
|
|
115
|
173
|
private let leftView: NSView
|
|
|
@@ -282,6 +340,8 @@ final class MyProfilePageView: NSView {
|
|
282
|
340
|
backButton.title = L("← All profiles")
|
|
283
|
341
|
saveButton.title = L("Save Profile →")
|
|
284
|
342
|
contextLabel.stringValue = editingProfileID == nil ? L("New profile") : L("Edit profile")
|
|
|
343
|
+ ProfileFormLocalization.refresh(in: formStack)
|
|
|
344
|
+ referralHelperLabel?.stringValue = L("If someone referred you for this job, enter their name or company here")
|
|
285
|
345
|
renumberWorkExperienceEntries()
|
|
286
|
346
|
renumberEducationEntries()
|
|
287
|
347
|
ProfileThemeAppearance.refreshFormSubtree(formStack)
|
|
|
@@ -454,22 +514,22 @@ final class MyProfilePageView: NSView {
|
|
454
|
514
|
])
|
|
455
|
515
|
|
|
456
|
516
|
addFullWidthArrangedSubview(
|
|
457
|
|
- labeledGroup(title: L("Profile Name *"), field: profileNameField, placeholder: L("Marketing Director Profile"))
|
|
|
517
|
+ labeledGroup(labelKey: "Profile Name *", field: profileNameField, placeholderKey: "Marketing Director Profile")
|
|
458
|
518
|
)
|
|
459
|
|
- addFullWidthArrangedSubview(sectionHeading(L("Personal Information")))
|
|
|
519
|
+ addFullWidthArrangedSubview(sectionHeading("Personal Information"))
|
|
460
|
520
|
|
|
461
|
|
- let nameGroup = labeledGroup(title: L("Full Name *"), field: fullNameField, placeholder: L("John Doe"))
|
|
462
|
|
- let emailGroup = labeledGroup(title: L("Email *"), field: emailField, placeholder: L("john@example.com"))
|
|
|
521
|
+ let nameGroup = labeledGroup(labelKey: "Full Name *", field: fullNameField, placeholderKey: "John Doe")
|
|
|
522
|
+ let emailGroup = labeledGroup(labelKey: "Email *", field: emailField, placeholderKey: "john@example.com")
|
|
463
|
523
|
nameEmailRow = ProfileDualFieldRow(left: nameGroup, right: emailGroup, spacing: 12)
|
|
464
|
524
|
addFullWidthArrangedSubview(nameEmailRow)
|
|
465
|
525
|
|
|
466
|
|
- let phoneGroup = labeledGroup(title: L("Phone"), field: phoneField, placeholder: L("+1 (555) 123-4567"))
|
|
467
|
|
- let jobGroup = labeledGroup(title: L("Job Title *"), field: jobTitleField, placeholder: L("Software Engineer"))
|
|
|
526
|
+ let phoneGroup = labeledGroup(labelKey: "Phone", field: phoneField, placeholderKey: "+1 (555) 123-4567")
|
|
|
527
|
+ let jobGroup = labeledGroup(labelKey: "Job Title *", field: jobTitleField, placeholderKey: "Software Engineer")
|
|
468
|
528
|
phoneJobRow = ProfileDualFieldRow(left: phoneGroup, right: jobGroup, spacing: 12)
|
|
469
|
529
|
addFullWidthArrangedSubview(phoneJobRow)
|
|
470
|
530
|
|
|
471
|
531
|
addFullWidthArrangedSubview(
|
|
472
|
|
- labeledGroup(title: L("Address"), field: addressField, placeholder: L("123 Main St, City, State, ZIP"))
|
|
|
532
|
+ labeledGroup(labelKey: "Address", field: addressField, placeholderKey: "123 Main St, City, State, ZIP")
|
|
473
|
533
|
)
|
|
474
|
534
|
addFullWidthArrangedSubview(careerSummaryBlock())
|
|
475
|
535
|
addFullWidthArrangedSubview(horizontalSeparator())
|
|
|
@@ -479,8 +539,8 @@ final class MyProfilePageView: NSView {
|
|
479
|
539
|
addFullWidthArrangedSubview(horizontalSeparator())
|
|
480
|
540
|
addFullWidthArrangedSubview(
|
|
481
|
541
|
multilineProfileBlock(
|
|
482
|
|
- title: L("Certificates / Rewards"),
|
|
483
|
|
- placeholder: L("List your certificates and awards..."),
|
|
|
542
|
+ labelKey: "Certificates / Rewards",
|
|
|
543
|
+ placeholderKey: "List your certificates and awards...",
|
|
484
|
544
|
field: certificatesField,
|
|
485
|
545
|
minHeight: 100
|
|
486
|
546
|
)
|
|
|
@@ -488,8 +548,8 @@ final class MyProfilePageView: NSView {
|
|
488
|
548
|
addFullWidthArrangedSubview(horizontalSeparator())
|
|
489
|
549
|
addFullWidthArrangedSubview(
|
|
490
|
550
|
multilineProfileBlock(
|
|
491
|
|
- title: L("Interests"),
|
|
492
|
|
- placeholder: L("List your interests and hobbies..."),
|
|
|
551
|
+ labelKey: "Interests",
|
|
|
552
|
+ placeholderKey: "List your interests and hobbies...",
|
|
493
|
553
|
field: interestsField,
|
|
494
|
554
|
minHeight: 100
|
|
495
|
555
|
)
|
|
|
@@ -497,8 +557,8 @@ final class MyProfilePageView: NSView {
|
|
497
|
557
|
addFullWidthArrangedSubview(horizontalSeparator())
|
|
498
|
558
|
addFullWidthArrangedSubview(
|
|
499
|
559
|
multilineProfileBlock(
|
|
500
|
|
- title: L("Languages"),
|
|
501
|
|
- placeholder: L("List languages you speak (e.g., English - Native, Spanish - Fluent)..."),
|
|
|
560
|
+ labelKey: "Languages",
|
|
|
561
|
+ placeholderKey: "List languages you speak (e.g., English - Native, Spanish - Fluent)...",
|
|
502
|
562
|
field: languagesField,
|
|
503
|
563
|
minHeight: 100
|
|
504
|
564
|
)
|
|
|
@@ -518,6 +578,7 @@ final class MyProfilePageView: NSView {
|
|
518
|
578
|
|
|
519
|
579
|
func prepareNewProfile() {
|
|
520
|
580
|
editingProfileID = nil
|
|
|
581
|
+ applyLocalizedStrings()
|
|
521
|
582
|
contextLabel.stringValue = L("New profile")
|
|
522
|
583
|
applyForm(
|
|
523
|
584
|
from: SavedProfile(
|
|
|
@@ -537,6 +598,7 @@ final class MyProfilePageView: NSView {
|
|
537
|
598
|
|
|
538
|
599
|
func loadSavedProfile(_ profile: SavedProfile) {
|
|
539
|
600
|
editingProfileID = profile.id
|
|
|
601
|
+ applyLocalizedStrings()
|
|
540
|
602
|
contextLabel.stringValue = L("Edit profile")
|
|
541
|
603
|
applyForm(from: profile)
|
|
542
|
604
|
}
|
|
|
@@ -658,8 +720,9 @@ final class MyProfilePageView: NSView {
|
|
658
|
720
|
view.widthAnchor.constraint(equalTo: formStack.widthAnchor).isActive = true
|
|
659
|
721
|
}
|
|
660
|
722
|
|
|
661
|
|
- private func sectionHeading(_ text: String) -> NSView {
|
|
662
|
|
- let label = NSTextField(labelWithString: text)
|
|
|
723
|
+ private func sectionHeading(_ key: String) -> NSView {
|
|
|
724
|
+ let label = NSTextField(labelWithString: L(key))
|
|
|
725
|
+ ProfileFormLocalization.tagSection(label, key: key)
|
|
663
|
726
|
label.font = .systemFont(ofSize: 15, weight: .semibold)
|
|
664
|
727
|
label.textColor = ProfilePagePalette.primaryText
|
|
665
|
728
|
label.baseWritingDirection = .leftToRight
|
|
|
@@ -685,15 +748,16 @@ final class MyProfilePageView: NSView {
|
|
685
|
748
|
return row
|
|
686
|
749
|
}
|
|
687
|
750
|
|
|
688
|
|
- private func labeledGroup(title: String, field: NSTextField, placeholder: String) -> NSView {
|
|
689
|
|
- let label = NSTextField(labelWithString: title)
|
|
|
751
|
+ private func labeledGroup(labelKey: String, field: NSTextField, placeholderKey: String) -> NSView {
|
|
|
752
|
+ let label = NSTextField(labelWithString: L(labelKey))
|
|
|
753
|
+ ProfileFormLocalization.tagLabel(label, key: labelKey)
|
|
690
|
754
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
691
|
755
|
label.textColor = ProfilePagePalette.secondaryText
|
|
692
|
756
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
693
|
757
|
label.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
694
|
758
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
|
|
695
|
759
|
|
|
696
|
|
- styleSingleLineField(field, placeholder: placeholder)
|
|
|
760
|
+ styleSingleLineField(field, placeholderKey: placeholderKey)
|
|
697
|
761
|
let wrap = roundedFieldChrome(containing: field, minHeight: 40)
|
|
698
|
762
|
|
|
699
|
763
|
let stack = NSStackView(views: [label, wrap])
|
|
|
@@ -715,7 +779,8 @@ final class MyProfilePageView: NSView {
|
|
715
|
779
|
return stack
|
|
716
|
780
|
}
|
|
717
|
781
|
|
|
718
|
|
- private func styleSingleLineField(_ field: NSTextField, placeholder: String) {
|
|
|
782
|
+ private func styleSingleLineField(_ field: NSTextField, placeholderKey: String) {
|
|
|
783
|
+ ProfileFormLocalization.tagPlaceholder(field, key: placeholderKey)
|
|
719
|
784
|
field.translatesAutoresizingMaskIntoConstraints = false
|
|
720
|
785
|
field.isBordered = false
|
|
721
|
786
|
field.drawsBackground = false
|
|
|
@@ -724,15 +789,7 @@ final class MyProfilePageView: NSView {
|
|
724
|
789
|
field.textColor = ProfilePagePalette.primaryText
|
|
725
|
790
|
field.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
726
|
791
|
field.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
727
|
|
- let paragraph = ProfileLayoutEnforcement.leftAlignedParagraphStyle()
|
|
728
|
|
- field.placeholderAttributedString = NSAttributedString(
|
|
729
|
|
- string: placeholder,
|
|
730
|
|
- attributes: [
|
|
731
|
|
- .foregroundColor: ProfilePagePalette.secondaryText,
|
|
732
|
|
- .font: NSFont.systemFont(ofSize: 14, weight: .regular),
|
|
733
|
|
- .paragraphStyle: paragraph
|
|
734
|
|
- ]
|
|
735
|
|
- )
|
|
|
792
|
+ ProfileFormLocalization.applyPlaceholder(L(placeholderKey), to: field)
|
|
736
|
793
|
field.cell?.usesSingleLineMode = true
|
|
737
|
794
|
field.cell?.wraps = false
|
|
738
|
795
|
field.cell?.isScrollable = true
|
|
|
@@ -763,6 +820,7 @@ final class MyProfilePageView: NSView {
|
|
763
|
820
|
|
|
764
|
821
|
private func careerSummaryBlock() -> NSView {
|
|
765
|
822
|
let label = NSTextField(labelWithString: L("Career Summary"))
|
|
|
823
|
+ ProfileFormLocalization.tagLabel(label, key: "Career Summary")
|
|
766
|
824
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
767
|
825
|
label.textColor = ProfilePagePalette.secondaryText
|
|
768
|
826
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -783,13 +841,13 @@ final class MyProfilePageView: NSView {
|
|
783
|
841
|
careerField.stringValue = ""
|
|
784
|
842
|
careerField.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
785
|
843
|
careerField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
786
|
|
- careerField.placeholderAttributedString = NSAttributedString(
|
|
787
|
|
- string: L("Brief overview of your professional background and key achievements..."),
|
|
788
|
|
- attributes: [
|
|
789
|
|
- .foregroundColor: ProfilePagePalette.secondaryText,
|
|
790
|
|
- .font: NSFont.systemFont(ofSize: 14, weight: .regular),
|
|
791
|
|
- .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
|
|
792
|
|
- ]
|
|
|
844
|
+ ProfileFormLocalization.tagPlaceholder(
|
|
|
845
|
+ careerField,
|
|
|
846
|
+ key: "Brief overview of your professional background and key achievements..."
|
|
|
847
|
+ )
|
|
|
848
|
+ ProfileFormLocalization.applyPlaceholder(
|
|
|
849
|
+ L("Brief overview of your professional background and key achievements..."),
|
|
|
850
|
+ to: careerField
|
|
793
|
851
|
)
|
|
794
|
852
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(careerField)
|
|
795
|
853
|
|
|
|
@@ -830,13 +888,15 @@ final class MyProfilePageView: NSView {
|
|
830
|
888
|
return stack
|
|
831
|
889
|
}
|
|
832
|
890
|
|
|
833
|
|
- private func multilineProfileBlock(title: String, placeholder: String, field: NSTextField, minHeight: CGFloat) -> NSView {
|
|
834
|
|
- let label = NSTextField(labelWithString: title)
|
|
|
891
|
+ private func multilineProfileBlock(labelKey: String, placeholderKey: String, field: NSTextField, minHeight: CGFloat) -> NSView {
|
|
|
892
|
+ let label = NSTextField(labelWithString: L(labelKey))
|
|
|
893
|
+ ProfileFormLocalization.tagLabel(label, key: labelKey)
|
|
835
|
894
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
836
|
895
|
label.textColor = ProfilePagePalette.secondaryText
|
|
837
|
896
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
838
|
897
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
|
|
839
|
898
|
|
|
|
899
|
+ ProfileFormLocalization.tagPlaceholder(field, key: placeholderKey)
|
|
840
|
900
|
field.translatesAutoresizingMaskIntoConstraints = false
|
|
841
|
901
|
field.isEditable = true
|
|
842
|
902
|
field.isSelectable = true
|
|
|
@@ -852,14 +912,7 @@ final class MyProfilePageView: NSView {
|
|
852
|
912
|
field.stringValue = ""
|
|
853
|
913
|
field.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
854
|
914
|
field.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
855
|
|
- field.placeholderAttributedString = NSAttributedString(
|
|
856
|
|
- string: placeholder,
|
|
857
|
|
- attributes: [
|
|
858
|
|
- .foregroundColor: ProfilePagePalette.secondaryText,
|
|
859
|
|
- .font: NSFont.systemFont(ofSize: 14, weight: .regular),
|
|
860
|
|
- .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
|
|
861
|
|
- ]
|
|
862
|
|
- )
|
|
|
915
|
+ ProfileFormLocalization.applyPlaceholder(L(placeholderKey), to: field)
|
|
863
|
916
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(field)
|
|
864
|
917
|
|
|
865
|
918
|
let wrap = NSView()
|
|
|
@@ -901,12 +954,13 @@ final class MyProfilePageView: NSView {
|
|
901
|
954
|
|
|
902
|
955
|
private func referralBlock() -> NSView {
|
|
903
|
956
|
let label = NSTextField(labelWithString: L("Referral (Optional)"))
|
|
|
957
|
+ ProfileFormLocalization.tagLabel(label, key: "Referral (Optional)")
|
|
904
|
958
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
905
|
959
|
label.textColor = ProfilePagePalette.secondaryText
|
|
906
|
960
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
907
|
961
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
|
|
908
|
962
|
|
|
909
|
|
- styleSingleLineField(referralField, placeholder: L("Referred by (Company/Person Name)"))
|
|
|
963
|
+ styleSingleLineField(referralField, placeholderKey: "Referred by (Company/Person Name)")
|
|
910
|
964
|
let wrap = roundedFieldChrome(containing: referralField, minHeight: 40)
|
|
911
|
965
|
|
|
912
|
966
|
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 {
|
|
947
|
1001
|
|
|
948
|
1002
|
private func workExperienceSection() -> NSView {
|
|
949
|
1003
|
let title = NSTextField(labelWithString: L("Work Experience"))
|
|
|
1004
|
+ ProfileFormLocalization.tagSection(title, key: "Work Experience")
|
|
950
|
1005
|
title.font = .systemFont(ofSize: 15, weight: .semibold)
|
|
951
|
1006
|
title.textColor = ProfilePagePalette.primaryText
|
|
952
|
1007
|
title.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -954,6 +1009,7 @@ final class MyProfilePageView: NSView {
|
|
954
|
1009
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(title)
|
|
955
|
1010
|
|
|
956
|
1011
|
let addButton = NSButton(title: L("+ Add Another"), target: self, action: #selector(didTapAddWorkExperience))
|
|
|
1012
|
+ ProfileFormLocalization.tagButton(addButton, key: "+ Add Another")
|
|
957
|
1013
|
addButton.translatesAutoresizingMaskIntoConstraints = false
|
|
958
|
1014
|
addButton.bezelStyle = .rounded
|
|
959
|
1015
|
addButton.isBordered = true
|
|
|
@@ -994,6 +1050,7 @@ final class MyProfilePageView: NSView {
|
|
994
|
1050
|
|
|
995
|
1051
|
private func educationSection() -> NSView {
|
|
996
|
1052
|
let title = NSTextField(labelWithString: L("Education"))
|
|
|
1053
|
+ ProfileFormLocalization.tagSection(title, key: "Education")
|
|
997
|
1054
|
title.font = .systemFont(ofSize: 15, weight: .semibold)
|
|
998
|
1055
|
title.textColor = ProfilePagePalette.primaryText
|
|
999
|
1056
|
title.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -1001,6 +1058,7 @@ final class MyProfilePageView: NSView {
|
|
1001
|
1058
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(title)
|
|
1002
|
1059
|
|
|
1003
|
1060
|
let addButton = NSButton(title: L("+ Add Another"), target: self, action: #selector(didTapAddEducation))
|
|
|
1061
|
+ ProfileFormLocalization.tagButton(addButton, key: "+ Add Another")
|
|
1004
|
1062
|
addButton.translatesAutoresizingMaskIntoConstraints = false
|
|
1005
|
1063
|
addButton.bezelStyle = .rounded
|
|
1006
|
1064
|
addButton.isBordered = true
|
|
|
@@ -1259,9 +1317,11 @@ private final class WorkExperienceEntryView: NSView {
|
|
1259
|
1317
|
if #available(macOS 11.0, *) {
|
|
1260
|
1318
|
deleteButton.image = NSImage(systemSymbolName: "trash", accessibilityDescription: L("Remove experience"))
|
|
1261
|
1319
|
deleteButton.imagePosition = .imageOnly
|
|
|
1320
|
+ ProfileFormLocalization.tagButton(deleteButton, key: "Remove experience")
|
|
1262
|
1321
|
} else {
|
|
1263
|
1322
|
deleteButton.title = L("Remove")
|
|
1264
|
1323
|
deleteButton.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
1324
|
+ ProfileFormLocalization.tagButton(deleteButton, key: "Remove")
|
|
1265
|
1325
|
}
|
|
1266
|
1326
|
|
|
1267
|
1327
|
let headerSpacer = NSView()
|
|
|
@@ -1276,15 +1336,27 @@ private final class WorkExperienceEntryView: NSView {
|
|
1276
|
1336
|
ProfileLayoutEnforcement.applyForcedLTR(to: headerRow)
|
|
1277
|
1337
|
headerRow.translatesAutoresizingMaskIntoConstraints = false
|
|
1278
|
1338
|
|
|
1279
|
|
- let jobGroup = Self.labeledFieldStack(title: L("Job Title *"), field: jobTitleField, placeholder: L("e.g., Software Engineer"))
|
|
1280
|
|
- let companyGroup = Self.labeledFieldStack(title: L("Company Name *"), field: companyField, placeholder: L("e.g., Google"))
|
|
|
1339
|
+ let jobGroup = Self.labeledFieldStack(
|
|
|
1340
|
+ labelKey: "Job Title *",
|
|
|
1341
|
+ field: jobTitleField,
|
|
|
1342
|
+ placeholderKey: "e.g., Software Engineer"
|
|
|
1343
|
+ )
|
|
|
1344
|
+ let companyGroup = Self.labeledFieldStack(
|
|
|
1345
|
+ labelKey: "Company Name *",
|
|
|
1346
|
+ field: companyField,
|
|
|
1347
|
+ placeholderKey: "e.g., Google"
|
|
|
1348
|
+ )
|
|
1281
|
1349
|
jobCompanyRow = ProfileDualFieldRow(left: jobGroup, right: companyGroup, spacing: 12)
|
|
1282
|
1350
|
|
|
1283
|
|
- let durationGroup = Self.labeledFieldStack(title: L("Duration *"), field: durationField, placeholder: L("e.g., Jan 2020 - Present"))
|
|
|
1351
|
+ let durationGroup = Self.labeledFieldStack(
|
|
|
1352
|
+ labelKey: "Duration *",
|
|
|
1353
|
+ field: durationField,
|
|
|
1354
|
+ placeholderKey: "e.g., Jan 2020 - Present"
|
|
|
1355
|
+ )
|
|
1284
|
1356
|
let descriptionGroup = Self.multilineLabeledStack(
|
|
1285
|
|
- title: L("Description"),
|
|
|
1357
|
+ labelKey: "Description",
|
|
1286
|
1358
|
field: descriptionField,
|
|
1287
|
|
- placeholder: L("Describe your responsibilities and achievements..."),
|
|
|
1359
|
+ placeholderKey: "Describe your responsibilities and achievements...",
|
|
1288
|
1360
|
minHeight: 120
|
|
1289
|
1361
|
)
|
|
1290
|
1362
|
|
|
|
@@ -1334,14 +1406,15 @@ private final class WorkExperienceEntryView: NSView {
|
|
1334
|
1406
|
ProfileThemeAppearance.refreshFormSubtree(self)
|
|
1335
|
1407
|
}
|
|
1336
|
1408
|
|
|
1337
|
|
- fileprivate static func labeledFieldStack(title: String, field: NSTextField, placeholder: String) -> NSView {
|
|
1338
|
|
- let label = NSTextField(labelWithString: title)
|
|
|
1409
|
+ fileprivate static func labeledFieldStack(labelKey: String, field: NSTextField, placeholderKey: String) -> NSView {
|
|
|
1410
|
+ let label = NSTextField(labelWithString: L(labelKey))
|
|
|
1411
|
+ ProfileFormLocalization.tagLabel(label, key: labelKey)
|
|
1339
|
1412
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
1340
|
1413
|
label.textColor = ProfilePagePalette.secondaryText
|
|
1341
|
1414
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
1342
|
1415
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
|
|
1343
|
1416
|
|
|
1344
|
|
- styleSingleLineField(field, placeholder: placeholder)
|
|
|
1417
|
+ styleSingleLineField(field, placeholderKey: placeholderKey)
|
|
1345
|
1418
|
let wrap = roundedChrome(around: field, minHeight: 40)
|
|
1346
|
1419
|
|
|
1347
|
1420
|
let stack = NSStackView(views: [label, wrap])
|
|
|
@@ -1362,13 +1435,15 @@ private final class WorkExperienceEntryView: NSView {
|
|
1362
|
1435
|
return stack
|
|
1363
|
1436
|
}
|
|
1364
|
1437
|
|
|
1365
|
|
- private static func multilineLabeledStack(title: String, field: NSTextField, placeholder: String, minHeight: CGFloat) -> NSView {
|
|
1366
|
|
- let label = NSTextField(labelWithString: title)
|
|
|
1438
|
+ private static func multilineLabeledStack(labelKey: String, field: NSTextField, placeholderKey: String, minHeight: CGFloat) -> NSView {
|
|
|
1439
|
+ let label = NSTextField(labelWithString: L(labelKey))
|
|
|
1440
|
+ ProfileFormLocalization.tagLabel(label, key: labelKey)
|
|
1367
|
1441
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
1368
|
1442
|
label.textColor = ProfilePagePalette.secondaryText
|
|
1369
|
1443
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
1370
|
1444
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
|
|
1371
|
1445
|
|
|
|
1446
|
+ ProfileFormLocalization.tagPlaceholder(field, key: placeholderKey)
|
|
1372
|
1447
|
field.translatesAutoresizingMaskIntoConstraints = false
|
|
1373
|
1448
|
field.isEditable = true
|
|
1374
|
1449
|
field.isSelectable = true
|
|
|
@@ -1381,14 +1456,7 @@ private final class WorkExperienceEntryView: NSView {
|
|
1381
|
1456
|
field.cell?.wraps = true
|
|
1382
|
1457
|
field.cell?.isScrollable = false
|
|
1383
|
1458
|
field.cell?.usesSingleLineMode = false
|
|
1384
|
|
- field.placeholderAttributedString = NSAttributedString(
|
|
1385
|
|
- string: placeholder,
|
|
1386
|
|
- attributes: [
|
|
1387
|
|
- .foregroundColor: ProfilePagePalette.secondaryText,
|
|
1388
|
|
- .font: NSFont.systemFont(ofSize: 14, weight: .regular),
|
|
1389
|
|
- .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
|
|
1390
|
|
- ]
|
|
1391
|
|
- )
|
|
|
1459
|
+ ProfileFormLocalization.applyPlaceholder(L(placeholderKey), to: field)
|
|
1392
|
1460
|
ProfileLayoutEnforcement.applyLeftAlignedTextField(field)
|
|
1393
|
1461
|
|
|
1394
|
1462
|
let wrap = NSView()
|
|
|
@@ -1428,21 +1496,15 @@ private final class WorkExperienceEntryView: NSView {
|
|
1428
|
1496
|
return stack
|
|
1429
|
1497
|
}
|
|
1430
|
1498
|
|
|
1431
|
|
- private static func styleSingleLineField(_ field: NSTextField, placeholder: String) {
|
|
|
1499
|
+ private static func styleSingleLineField(_ field: NSTextField, placeholderKey: String) {
|
|
|
1500
|
+ ProfileFormLocalization.tagPlaceholder(field, key: placeholderKey)
|
|
1432
|
1501
|
field.translatesAutoresizingMaskIntoConstraints = false
|
|
1433
|
1502
|
field.isBordered = false
|
|
1434
|
1503
|
field.drawsBackground = false
|
|
1435
|
1504
|
field.focusRingType = .none
|
|
1436
|
1505
|
field.font = .systemFont(ofSize: 14, weight: .regular)
|
|
1437
|
1506
|
field.textColor = ProfilePagePalette.primaryText
|
|
1438
|
|
- field.placeholderAttributedString = NSAttributedString(
|
|
1439
|
|
- string: placeholder,
|
|
1440
|
|
- attributes: [
|
|
1441
|
|
- .foregroundColor: ProfilePagePalette.secondaryText,
|
|
1442
|
|
- .font: NSFont.systemFont(ofSize: 14, weight: .regular),
|
|
1443
|
|
- .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
|
|
1444
|
|
- ]
|
|
1445
|
|
- )
|
|
|
1507
|
+ ProfileFormLocalization.applyPlaceholder(L(placeholderKey), to: field)
|
|
1446
|
1508
|
field.cell?.usesSingleLineMode = true
|
|
1447
|
1509
|
field.cell?.wraps = false
|
|
1448
|
1510
|
field.cell?.isScrollable = true
|
|
|
@@ -1549,9 +1611,11 @@ private final class EducationEntryView: NSView {
|
|
1549
|
1611
|
if #available(macOS 11.0, *) {
|
|
1550
|
1612
|
deleteButton.image = NSImage(systemSymbolName: "trash", accessibilityDescription: L("Remove education"))
|
|
1551
|
1613
|
deleteButton.imagePosition = .imageOnly
|
|
|
1614
|
+ ProfileFormLocalization.tagButton(deleteButton, key: "Remove education")
|
|
1552
|
1615
|
} else {
|
|
1553
|
1616
|
deleteButton.title = L("Remove")
|
|
1554
|
1617
|
deleteButton.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
1618
|
+ ProfileFormLocalization.tagButton(deleteButton, key: "Remove")
|
|
1555
|
1619
|
}
|
|
1556
|
1620
|
|
|
1557
|
1621
|
let headerSpacer = NSView()
|
|
|
@@ -1567,21 +1631,21 @@ private final class EducationEntryView: NSView {
|
|
1567
|
1631
|
headerRow.translatesAutoresizingMaskIntoConstraints = false
|
|
1568
|
1632
|
|
|
1569
|
1633
|
let degreeGroup = WorkExperienceEntryView.labeledFieldStack(
|
|
1570
|
|
- title: L("Degree / program *"),
|
|
|
1634
|
+ labelKey: "Degree / program *",
|
|
1571
|
1635
|
field: degreeField,
|
|
1572
|
|
- placeholder: L("e.g., BSc Computer Science")
|
|
|
1636
|
+ placeholderKey: "e.g., BSc Computer Science"
|
|
1573
|
1637
|
)
|
|
1574
|
1638
|
let institutionGroup = WorkExperienceEntryView.labeledFieldStack(
|
|
1575
|
|
- title: L("Institution *"),
|
|
|
1639
|
+ labelKey: "Institution *",
|
|
1576
|
1640
|
field: institutionField,
|
|
1577
|
|
- placeholder: L("e.g., MIT")
|
|
|
1641
|
+ placeholderKey: "e.g., MIT"
|
|
1578
|
1642
|
)
|
|
1579
|
1643
|
degreeInstitutionRow = ProfileDualFieldRow(left: degreeGroup, right: institutionGroup, spacing: 12)
|
|
1580
|
1644
|
|
|
1581
|
1645
|
let yearGroup = WorkExperienceEntryView.labeledFieldStack(
|
|
1582
|
|
- title: L("Year *"),
|
|
|
1646
|
+ labelKey: "Year *",
|
|
1583
|
1647
|
field: yearField,
|
|
1584
|
|
- placeholder: L("e.g., 2020")
|
|
|
1648
|
+ placeholderKey: "e.g., 2020"
|
|
1585
|
1649
|
)
|
|
1586
|
1650
|
|
|
1587
|
1651
|
let inner = NSStackView(views: [headerRow, degreeInstitutionRow, yearGroup])
|