浏览代码

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 1 月之前
父节点
当前提交
e413b324bc

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

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

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

@@ -227,6 +227,7 @@ final class CVProfileDocumentView: NSView {
 
         let root = buildRoot()
         root.translatesAutoresizingMaskIntoConstraints = false
+        enforceLeftToRightLayout(on: root)
         card.addSubview(root)
 
         addSubview(card)
@@ -1562,11 +1563,15 @@ final class CVProfileDocumentView: NSView {
         case .slashed: s = "// \(upper)"
         case .bracketed: s = "[ \(upper) ]"
         }
-        let t = NSTextField(labelWithString: s)
-        t.font = style.sectionFont
-        t.textColor = style.sectionInk
-        t.alignment = .left
-        return t
+        return label(s, font: style.sectionFont, color: style.sectionInk, maxLines: 1)
+    }
+
+    /// Résumé templates are designed LTR (columns, rails, sidebars). Keep layout LTR even when UI copy is Arabic.
+    private func enforceLeftToRightLayout(on view: NSView) {
+        view.userInterfaceLayoutDirection = .leftToRight
+        for child in view.subviews {
+            enforceLeftToRightLayout(on: child)
+        }
     }
 
     private func label(_ string: String, font: NSFont, color: NSColor, maxLines: Int) -> NSTextField {
@@ -1579,12 +1584,27 @@ final class CVProfileDocumentView: NSView {
             t = NSTextField(labelWithString: string)
             t.maximumNumberOfLines = maxLines
         }
+        t.translatesAutoresizingMaskIntoConstraints = false
         t.font = font
         t.textColor = color
         t.alignment = .left
+        applyLeftToRightTextLayout(to: t)
         return t
     }
 
+    /// Mixed Arabic/English body copy still measures and wraps in column width when base direction is LTR.
+    private func applyLeftToRightTextLayout(to field: NSTextField) {
+        let paragraph = NSMutableParagraphStyle()
+        paragraph.alignment = field.alignment
+        paragraph.baseWritingDirection = .leftToRight
+        paragraph.lineBreakMode = field.maximumNumberOfLines == 0 ? .byWordWrapping : .byTruncatingTail
+        field.attributedStringValue = NSAttributedString(string: field.stringValue, attributes: [
+            .font: field.font ?? .systemFont(ofSize: 12),
+            .foregroundColor: field.textColor ?? .labelColor,
+            .paragraphStyle: paragraph
+        ])
+    }
+
     private func displayable(_ value: String, placeholder: String) -> String {
         let t = value.trimmingCharacters(in: .whitespacesAndNewlines)
         return t.isEmpty ? placeholder : t

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

@@ -71,6 +71,7 @@ final class CVTemplatePreviewView: NSView {
         super.init(frame: .zero)
         wantsLayer = true
         translatesAutoresizingMaskIntoConstraints = false
+        userInterfaceLayoutDirection = .leftToRight
         configurePaper()
     }
 
@@ -100,6 +101,7 @@ final class CVTemplatePreviewView: NSView {
 
         let root = buildResumeRoot()
         root.translatesAutoresizingMaskIntoConstraints = false
+        enforceLeftToRightLayout(on: root)
         paper.addSubview(root)
         NSLayoutConstraint.activate([
             root.leadingAnchor.constraint(equalTo: paper.leadingAnchor, constant: 7),
@@ -894,7 +896,7 @@ final class CVTemplatePreviewView: NSView {
         row.orientation = .horizontal
         row.spacing = 5
         row.translatesAutoresizingMaskIntoConstraints = false
-        let lab = makeLabel("PORTFOLIO SNAPSHOT", font: .systemFont(ofSize: 6.5, weight: .black), color: palette.previewInk, alignment: .left, maxLines: 1)
+        let lab = makeLabel(L("PORTFOLIO SNAPSHOT"), font: .systemFont(ofSize: 6.5, weight: .black), color: palette.previewInk, alignment: .left, maxLines: 1)
         row.addArrangedSubview(stripe)
         row.addArrangedSubview(lab)
         v.addSubview(row)
@@ -1002,6 +1004,13 @@ final class CVTemplatePreviewView: NSView {
         return row
     }
 
+    private func enforceLeftToRightLayout(on view: NSView) {
+        view.userInterfaceLayoutDirection = .leftToRight
+        for child in view.subviews {
+            enforceLeftToRightLayout(on: child)
+        }
+    }
+
     private func makeLabel(
         _ text: String,
         font: NSFont,
@@ -1016,6 +1025,7 @@ final class CVTemplatePreviewView: NSView {
             tf = NSTextField(wrappingLabelWithString: text)
             tf.maximumNumberOfLines = maxLines
         }
+        tf.translatesAutoresizingMaskIntoConstraints = false
         tf.font = font
         tf.textColor = color
         tf.alignment = alignment
@@ -1024,6 +1034,15 @@ final class CVTemplatePreviewView: NSView {
         tf.drawsBackground = false
         tf.isBordered = false
         tf.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
+        let paragraph = NSMutableParagraphStyle()
+        paragraph.alignment = alignment
+        paragraph.baseWritingDirection = .leftToRight
+        paragraph.lineBreakMode = maxLines == 1 ? .byTruncatingTail : .byWordWrapping
+        tf.attributedStringValue = NSAttributedString(string: text, attributes: [
+            .font: font,
+            .foregroundColor: color,
+            .paragraphStyle: paragraph
+        ])
         return tf
     }
 }

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

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

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

@@ -317,6 +317,9 @@
 "SUMMARY" = "SUMMARY";
 "PROFESSIONAL SUMMARY" = "PROFESSIONAL SUMMARY";
 "SELECTED EXPERIENCE" = "SELECTED EXPERIENCE";
+"CORE COMPETENCIES" = "CORE COMPETENCIES";
+"TOOLS" = "TOOLS";
+"IMPACT" = "IMPACT";
 
 // MARK: - Job Browser
 "Return to the previous screen" = "Return to the previous screen";