@@ -345,6 +345,10 @@ fn source_contains_l10n_markers(source: &[u8]) -> bool {
345345 || bytes_contains ( source, b"LocalizedStringKey" )
346346 || bytes_contains ( source, b"Text(\" " )
347347 || bytes_contains ( source, b"Text(." )
348+ || bytes_contains ( source, b"Text(i18n:" )
349+ || bytes_contains ( source, b"String(i18n:" )
350+ || bytes_contains ( source, b"i18n:" )
351+ || bytes_contains ( source, b".translation" )
348352 || bytes_contains ( source, b"Localizable" )
349353}
350354
@@ -398,7 +402,7 @@ pub fn extract_swift(
398402 // Parse once, share tree across all enrichment passes.
399403 // Check which enrichment passes are needed before parsing
400404 let has_swiftui = source_contains_swiftui_markers ( source) ;
401- let has_l10n = source_contains_l10n_markers ( source) ;
405+ let has_l10n = has_swiftui || source_contains_l10n_markers ( source) ;
402406 let has_assets = source_contains_asset_markers ( source) ;
403407 let needs_doc = result. nodes . iter ( ) . any ( |n| n. doc_comment . is_none ( ) ) ;
404408 let needs_parse = needs_doc || has_swiftui || has_l10n || has_assets;
@@ -506,7 +510,7 @@ fn enrich_fallback_result(
506510 result : & mut ExtractionResult ,
507511) -> anyhow:: Result < ( ) > {
508512 let has_swiftui = source_contains_swiftui_markers ( source) ;
509- let has_l10n = source_contains_l10n_markers ( source) ;
513+ let has_l10n = has_swiftui || source_contains_l10n_markers ( source) ;
510514 let has_assets = source_contains_asset_markers ( source) ;
511515 let needs_tree = has_swiftui || has_l10n || has_assets;
512516
@@ -756,9 +760,10 @@ mod discovery_cache_tests {
756760#[ cfg( test) ]
757761mod marker_tests {
758762 use super :: {
759- source_contains_asset_markers , source_contains_l10n_markers ,
760- source_contains_swiftui_markers,
763+ extract_swift_via_fallback_for_tests , source_contains_asset_markers ,
764+ source_contains_l10n_markers , source_contains_swiftui_markers,
761765 } ;
766+ use std:: path:: Path ;
762767
763768 #[ test]
764769 fn swiftui_markers_ignore_plain_imports ( ) {
@@ -804,9 +809,48 @@ mod marker_tests {
804809 assert ! ( source_contains_l10n_markers(
805810 br#"Text(.accountForgetPassword)"#
806811 ) ) ;
812+ assert ! ( source_contains_l10n_markers( br#"Text(i18n: .commonShare)"# ) ) ;
813+ assert ! ( source_contains_l10n_markers(
814+ br#"String(i18n: .commonShare)"#
815+ ) ) ;
807816 assert ! ( source_contains_l10n_markers(
808817 br#"NSLocalizedString("hello", comment: "")"#
809818 ) ) ;
810819 assert ! ( source_contains_asset_markers( br#"Image("logo")"# ) ) ;
811820 }
821+
822+ #[ test]
823+ fn fallback_runs_l10n_enrichment_for_swiftui_custom_views_without_explicit_markers ( ) {
824+ let source = br#"
825+ import SwiftUI
826+
827+ struct TitleRow: View {
828+ let title: String
829+
830+ var body: some View {
831+ Text(title)
832+ }
833+ }
834+
835+ struct ContentView: View {
836+ let title: String
837+
838+ var body: some View {
839+ TitleRow(title: title)
840+ }
841+ }
842+ "# ;
843+
844+ let result =
845+ extract_swift_via_fallback_for_tests ( source, Path :: new ( "ContentView.swift" ) ) . unwrap ( ) ;
846+
847+ assert ! (
848+ result. nodes. iter( ) . any( |node| {
849+ node. metadata
850+ . get( "l10n.ref_kind" )
851+ . is_some_and( |value| value == "possible_string" )
852+ } ) ,
853+ "SwiftUI files should still receive l10n enrichment even without explicit L10n markers"
854+ ) ;
855+ }
812856}
0 commit comments