Переглянути джерело

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";