Parcourir la Source

Fix Arabic CV templates breaking RTL layout mirroring.

Stop setting AppleLanguages for app locale so CV columns and sidebars stay LTR while Arabic copy still loads from ar.lproj.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 il y a 4 jours
Parent
commit
e413b324bc

+ 3 - 1
App for Indeed/Services/AppLocalization.swift

@@ -77,6 +77,8 @@ final class AppLanguageManager {
77 77
     }
78 78
 
79 79
     func applyStoredPreferenceOnLaunch() {
80
+        // UI copy uses `appLocalized`; do not drive layout from `AppleLanguages` (Arabic forces RTL and breaks CV templates).
81
+        UserDefaults.standard.removeObject(forKey: "AppleLanguages")
80 82
         if UserDefaults.standard.string(forKey: UserDefaultsKey.preferredLanguage) == nil {
81 83
             setLanguage(AppLanguage.systemLanguage, notify: false)
82 84
         }
@@ -85,7 +87,7 @@ final class AppLanguageManager {
85 87
     func setLanguage(_ language: AppLanguage, notify: Bool = true) {
86 88
         let code = language.localeIdentifier
87 89
         UserDefaults.standard.set(code, forKey: UserDefaultsKey.preferredLanguage)
88
-        UserDefaults.standard.set([code], forKey: "AppleLanguages")
90
+        UserDefaults.standard.removeObject(forKey: "AppleLanguages")
89 91
         if notify {
90 92
             NotificationCenter.default.post(name: Self.didChangeNotification, object: self)
91 93
         }

+ 25 - 5
App for Indeed/Views/CVProfileDocumentView.swift

@@ -227,6 +227,7 @@ final class CVProfileDocumentView: NSView {
227 227
 
228 228
         let root = buildRoot()
229 229
         root.translatesAutoresizingMaskIntoConstraints = false
230
+        enforceLeftToRightLayout(on: root)
230 231
         card.addSubview(root)
231 232
 
232 233
         addSubview(card)
@@ -1562,11 +1563,15 @@ final class CVProfileDocumentView: NSView {
1562 1563
         case .slashed: s = "// \(upper)"
1563 1564
         case .bracketed: s = "[ \(upper) ]"
1564 1565
         }
1565
-        let t = NSTextField(labelWithString: s)
1566
-        t.font = style.sectionFont
1567
-        t.textColor = style.sectionInk
1568
-        t.alignment = .left
1569
-        return t
1566
+        return label(s, font: style.sectionFont, color: style.sectionInk, maxLines: 1)
1567
+    }
1568
+
1569
+    /// Résumé templates are designed LTR (columns, rails, sidebars). Keep layout LTR even when UI copy is Arabic.
1570
+    private func enforceLeftToRightLayout(on view: NSView) {
1571
+        view.userInterfaceLayoutDirection = .leftToRight
1572
+        for child in view.subviews {
1573
+            enforceLeftToRightLayout(on: child)
1574
+        }
1570 1575
     }
1571 1576
 
1572 1577
     private func label(_ string: String, font: NSFont, color: NSColor, maxLines: Int) -> NSTextField {
@@ -1579,12 +1584,27 @@ final class CVProfileDocumentView: NSView {
1579 1584
             t = NSTextField(labelWithString: string)
1580 1585
             t.maximumNumberOfLines = maxLines
1581 1586
         }
1587
+        t.translatesAutoresizingMaskIntoConstraints = false
1582 1588
         t.font = font
1583 1589
         t.textColor = color
1584 1590
         t.alignment = .left
1591
+        applyLeftToRightTextLayout(to: t)
1585 1592
         return t
1586 1593
     }
1587 1594
 
1595
+    /// Mixed Arabic/English body copy still measures and wraps in column width when base direction is LTR.
1596
+    private func applyLeftToRightTextLayout(to field: NSTextField) {
1597
+        let paragraph = NSMutableParagraphStyle()
1598
+        paragraph.alignment = field.alignment
1599
+        paragraph.baseWritingDirection = .leftToRight
1600
+        paragraph.lineBreakMode = field.maximumNumberOfLines == 0 ? .byWordWrapping : .byTruncatingTail
1601
+        field.attributedStringValue = NSAttributedString(string: field.stringValue, attributes: [
1602
+            .font: field.font ?? .systemFont(ofSize: 12),
1603
+            .foregroundColor: field.textColor ?? .labelColor,
1604
+            .paragraphStyle: paragraph
1605
+        ])
1606
+    }
1607
+
1588 1608
     private func displayable(_ value: String, placeholder: String) -> String {
1589 1609
         let t = value.trimmingCharacters(in: .whitespacesAndNewlines)
1590 1610
         return t.isEmpty ? placeholder : t

+ 20 - 1
App for Indeed/Views/CVTemplateMiniPreview.swift

@@ -71,6 +71,7 @@ final class CVTemplatePreviewView: NSView {
71 71
         super.init(frame: .zero)
72 72
         wantsLayer = true
73 73
         translatesAutoresizingMaskIntoConstraints = false
74
+        userInterfaceLayoutDirection = .leftToRight
74 75
         configurePaper()
75 76
     }
76 77
 
@@ -100,6 +101,7 @@ final class CVTemplatePreviewView: NSView {
100 101
 
101 102
         let root = buildResumeRoot()
102 103
         root.translatesAutoresizingMaskIntoConstraints = false
104
+        enforceLeftToRightLayout(on: root)
103 105
         paper.addSubview(root)
104 106
         NSLayoutConstraint.activate([
105 107
             root.leadingAnchor.constraint(equalTo: paper.leadingAnchor, constant: 7),
@@ -894,7 +896,7 @@ final class CVTemplatePreviewView: NSView {
894 896
         row.orientation = .horizontal
895 897
         row.spacing = 5
896 898
         row.translatesAutoresizingMaskIntoConstraints = false
897
-        let lab = makeLabel("PORTFOLIO SNAPSHOT", font: .systemFont(ofSize: 6.5, weight: .black), color: palette.previewInk, alignment: .left, maxLines: 1)
899
+        let lab = makeLabel(L("PORTFOLIO SNAPSHOT"), font: .systemFont(ofSize: 6.5, weight: .black), color: palette.previewInk, alignment: .left, maxLines: 1)
898 900
         row.addArrangedSubview(stripe)
899 901
         row.addArrangedSubview(lab)
900 902
         v.addSubview(row)
@@ -1002,6 +1004,13 @@ final class CVTemplatePreviewView: NSView {
1002 1004
         return row
1003 1005
     }
1004 1006
 
1007
+    private func enforceLeftToRightLayout(on view: NSView) {
1008
+        view.userInterfaceLayoutDirection = .leftToRight
1009
+        for child in view.subviews {
1010
+            enforceLeftToRightLayout(on: child)
1011
+        }
1012
+    }
1013
+
1005 1014
     private func makeLabel(
1006 1015
         _ text: String,
1007 1016
         font: NSFont,
@@ -1016,6 +1025,7 @@ final class CVTemplatePreviewView: NSView {
1016 1025
             tf = NSTextField(wrappingLabelWithString: text)
1017 1026
             tf.maximumNumberOfLines = maxLines
1018 1027
         }
1028
+        tf.translatesAutoresizingMaskIntoConstraints = false
1019 1029
         tf.font = font
1020 1030
         tf.textColor = color
1021 1031
         tf.alignment = alignment
@@ -1024,6 +1034,15 @@ final class CVTemplatePreviewView: NSView {
1024 1034
         tf.drawsBackground = false
1025 1035
         tf.isBordered = false
1026 1036
         tf.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
1037
+        let paragraph = NSMutableParagraphStyle()
1038
+        paragraph.alignment = alignment
1039
+        paragraph.baseWritingDirection = .leftToRight
1040
+        paragraph.lineBreakMode = maxLines == 1 ? .byTruncatingTail : .byWordWrapping
1041
+        tf.attributedStringValue = NSAttributedString(string: text, attributes: [
1042
+            .font: font,
1043
+            .foregroundColor: color,
1044
+            .paragraphStyle: paragraph
1045
+        ])
1027 1046
         return tf
1028 1047
     }
1029 1048
 }

+ 3 - 0
App for Indeed/ar.lproj/Localizable.strings

@@ -317,6 +317,9 @@
317 317
 "SUMMARY" = "الملخص";
318 318
 "PROFESSIONAL SUMMARY" = "الملخص المهني";
319 319
 "SELECTED EXPERIENCE" = "الخبرة المختارة";
320
+"CORE COMPETENCIES" = "الكفاءات الأساسية";
321
+"TOOLS" = "الأدوات";
322
+"IMPACT" = "التأثير";
320 323
 
321 324
 // MARK: - متصفح الوظائف
322 325
 "Return to the previous screen" = "العودة إلى الشاشة السابقة";

+ 3 - 0
App for Indeed/en.lproj/Localizable.strings

@@ -317,6 +317,9 @@
317 317
 "SUMMARY" = "SUMMARY";
318 318
 "PROFESSIONAL SUMMARY" = "PROFESSIONAL SUMMARY";
319 319
 "SELECTED EXPERIENCE" = "SELECTED EXPERIENCE";
320
+"CORE COMPETENCIES" = "CORE COMPETENCIES";
321
+"TOOLS" = "TOOLS";
322
+"IMPACT" = "IMPACT";
320 323
 
321 324
 // MARK: - Job Browser
322 325
 "Return to the previous screen" = "Return to the previous screen";