Просмотр исходного кода

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 месяц назад
Родитель
Сommit
e413b324bc

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

@@ -77,6 +77,8 @@ final class AppLanguageManager {
     }
     }
 
 
     func applyStoredPreferenceOnLaunch() {
     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 {
         if UserDefaults.standard.string(forKey: UserDefaultsKey.preferredLanguage) == nil {
             setLanguage(AppLanguage.systemLanguage, notify: false)
             setLanguage(AppLanguage.systemLanguage, notify: false)
         }
         }
@@ -85,7 +87,7 @@ final class AppLanguageManager {
     func setLanguage(_ language: AppLanguage, notify: Bool = true) {
     func setLanguage(_ language: AppLanguage, notify: Bool = true) {
         let code = language.localeIdentifier
         let code = language.localeIdentifier
         UserDefaults.standard.set(code, forKey: UserDefaultsKey.preferredLanguage)
         UserDefaults.standard.set(code, forKey: UserDefaultsKey.preferredLanguage)
-        UserDefaults.standard.set([code], forKey: "AppleLanguages")
+        UserDefaults.standard.removeObject(forKey: "AppleLanguages")
         if notify {
         if notify {
             NotificationCenter.default.post(name: Self.didChangeNotification, object: self)
             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()
         let root = buildRoot()
         root.translatesAutoresizingMaskIntoConstraints = false
         root.translatesAutoresizingMaskIntoConstraints = false
+        enforceLeftToRightLayout(on: root)
         card.addSubview(root)
         card.addSubview(root)
 
 
         addSubview(card)
         addSubview(card)
@@ -1562,11 +1563,15 @@ final class CVProfileDocumentView: NSView {
         case .slashed: s = "// \(upper)"
         case .slashed: s = "// \(upper)"
         case .bracketed: 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 {
     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 = NSTextField(labelWithString: string)
             t.maximumNumberOfLines = maxLines
             t.maximumNumberOfLines = maxLines
         }
         }
+        t.translatesAutoresizingMaskIntoConstraints = false
         t.font = font
         t.font = font
         t.textColor = color
         t.textColor = color
         t.alignment = .left
         t.alignment = .left
+        applyLeftToRightTextLayout(to: t)
         return 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 {
     private func displayable(_ value: String, placeholder: String) -> String {
         let t = value.trimmingCharacters(in: .whitespacesAndNewlines)
         let t = value.trimmingCharacters(in: .whitespacesAndNewlines)
         return t.isEmpty ? placeholder : t
         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)
         super.init(frame: .zero)
         wantsLayer = true
         wantsLayer = true
         translatesAutoresizingMaskIntoConstraints = false
         translatesAutoresizingMaskIntoConstraints = false
+        userInterfaceLayoutDirection = .leftToRight
         configurePaper()
         configurePaper()
     }
     }
 
 
@@ -100,6 +101,7 @@ final class CVTemplatePreviewView: NSView {
 
 
         let root = buildResumeRoot()
         let root = buildResumeRoot()
         root.translatesAutoresizingMaskIntoConstraints = false
         root.translatesAutoresizingMaskIntoConstraints = false
+        enforceLeftToRightLayout(on: root)
         paper.addSubview(root)
         paper.addSubview(root)
         NSLayoutConstraint.activate([
         NSLayoutConstraint.activate([
             root.leadingAnchor.constraint(equalTo: paper.leadingAnchor, constant: 7),
             root.leadingAnchor.constraint(equalTo: paper.leadingAnchor, constant: 7),
@@ -894,7 +896,7 @@ final class CVTemplatePreviewView: NSView {
         row.orientation = .horizontal
         row.orientation = .horizontal
         row.spacing = 5
         row.spacing = 5
         row.translatesAutoresizingMaskIntoConstraints = false
         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(stripe)
         row.addArrangedSubview(lab)
         row.addArrangedSubview(lab)
         v.addSubview(row)
         v.addSubview(row)
@@ -1002,6 +1004,13 @@ final class CVTemplatePreviewView: NSView {
         return row
         return row
     }
     }
 
 
+    private func enforceLeftToRightLayout(on view: NSView) {
+        view.userInterfaceLayoutDirection = .leftToRight
+        for child in view.subviews {
+            enforceLeftToRightLayout(on: child)
+        }
+    }
+
     private func makeLabel(
     private func makeLabel(
         _ text: String,
         _ text: String,
         font: NSFont,
         font: NSFont,
@@ -1016,6 +1025,7 @@ final class CVTemplatePreviewView: NSView {
             tf = NSTextField(wrappingLabelWithString: text)
             tf = NSTextField(wrappingLabelWithString: text)
             tf.maximumNumberOfLines = maxLines
             tf.maximumNumberOfLines = maxLines
         }
         }
+        tf.translatesAutoresizingMaskIntoConstraints = false
         tf.font = font
         tf.font = font
         tf.textColor = color
         tf.textColor = color
         tf.alignment = alignment
         tf.alignment = alignment
@@ -1024,6 +1034,15 @@ final class CVTemplatePreviewView: NSView {
         tf.drawsBackground = false
         tf.drawsBackground = false
         tf.isBordered = false
         tf.isBordered = false
         tf.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
         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
         return tf
     }
     }
 }
 }

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

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

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

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