ソースを参照

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 4 日 前
コミット
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";