Class: XCRes::StringsAnalyzer
- Defined in:
- lib/xcres/analyzer/strings_analyzer.rb
Overview
A StringsAnalyzer
scans the project for resources, which should be included in the output file.
Instance Attribute Summary collapse
-
#default_language ⇒ String
Optional two-letter language code conforming ISO 639-1.
Attributes inherited from Analyzer
#exclude_file_patterns, #logger, #options, #sections, #target
Instance Method Summary collapse
-
#absolute_info_plist_paths ⇒ Set<Pathname>
Absolute file paths to Info.plist files by build settings.
-
#absolute_project_file_path(file_path) ⇒ Pathname
Calculate the absolute path for a file path given relative to the project / its ‘$SRCROOT`.
- #analyze ⇒ Object
-
#build_section ⇒ Section
Build the section.
-
#derive_used_languages(strings_file_refs) ⇒ Set<String>
Derive the used languages from given strings files.
-
#info_plist_paths ⇒ Set<Pathname>
Discover Info.plist files by build settings of the application target.
-
#initialize(project = nil, options = {}) ⇒ StringsAnalyzer
constructor
Initialize a new analyzer.
-
#keys_by_file(path) ⇒ Hash{String => Hash}
Read a file and collect all its keys.
-
#languages ⇒ Set<String>
Find preferred languages, which is: - either only the default_language, if specified - or the intersection of native development and used languages - or all used languages.
-
#native_dev_languages ⇒ Set<String>
Find the native development languages by trying to use the “Localization native development region” from Info.plist.
-
#read_plist_key(path, key) ⇒ String
Extracts a given key from a plist file given as a path.
-
#read_strings_file(path) ⇒ Hash
Read a .strings file given as a path.
-
#selected_strings_file_refs ⇒ Array<PBXFileReference>
Select strings files by language.
-
#strings_file_refs ⇒ Array<PBXFileReference>
Discover all references to .strings files in project (e.g. Localizable.strings).
-
#used_languages ⇒ Set<String>
All used languages in the project.
Methods inherited from Analyzer
#filter_exclusions, #find_file_refs_by_extname, #is_file_ref_included_in_application_target?, #new_section, #project, #resources_files
Methods included from FileHelper
Constructor Details
#initialize(project = nil, options = {}) ⇒ StringsAnalyzer
Initialize a new analyzer
24 25 26 27 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 24 def initialize(project=nil, ={}) super self.default_language = [:default_language] end |
Instance Attribute Details
#default_language ⇒ String
Returns optional two-letter language code conforming ISO 639-1.
13 14 15 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 13 def default_language @default_language end |
Instance Method Details
#absolute_info_plist_paths ⇒ Set<Pathname>
Absolute file paths to Info.plist files by build settings. See #info_plist_paths.
136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 136 def absolute_info_plist_paths info_plist_paths.map do |path| absolute_project_file_path(path) end.select do |path| if path.to_s.include?('$') warn "Couldn't resolve all placeholders in INFOPLIST_FILE %s.", path.to_s false else true end end end |
#absolute_project_file_path(file_path) ⇒ Pathname
Calculate the absolute path for a file path given relative to the project / its ‘$SRCROOT`.
We need either absolute paths or relative paths to our current location. Xcodeproj provides this for PBXFileReference
, but this doesn’t work for file references in build settings.
214 215 216 217 218 219 220 221 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 214 def absolute_project_file_path(file_path) source_root = (project.path + '..').realpath if file_path.to_s.include?('$') Pathname(file_path.to_s.gsub(/\$[({]?SRCROOT[)}]?/, source_root.to_s)) else source_root + file_path end end |
#analyze ⇒ Object
29 30 31 32 33 34 35 36 37 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 29 def analyze log 'Strings files in project: %s', strings_file_refs.map(&:path) log 'Native development languages: %s', native_dev_languages.to_a log 'Used languages for .strings files: %s', used_languages.to_a log 'Preferred languages: %s', languages.to_a log 'Strings files after language selection: %s', selected_strings_file_refs.map(&:path) @sections = [build_section] end |
#build_section ⇒ Section
Build the section
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 43 def build_section selected_file_refs = selected_strings_file_refs # Apply ignore list file_paths = filter_exclusions(selected_file_refs.map(&:path)) filtered_file_refs = selected_file_refs.select { |file_ref| file_paths.include? file_ref.path } rel_file_paths = filtered_file_refs.map { |p| p.real_path.relative_path_from(Pathname.pwd) } log 'Non-ignored .strings files: %s', rel_file_paths.map(&:to_s) keys_by_file = {} for path in rel_file_paths keys_by_file[path] = keys_by_file(path) end items = keys_by_file.values.reduce({}, :merge) new_section('Strings', items) end |
#derive_used_languages(strings_file_refs) ⇒ Set<String>
Derive the used languages from given strings files
84 85 86 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 84 def derive_used_languages(strings_file_refs) strings_file_refs.map(&:name).to_set end |
#info_plist_paths ⇒ Set<Pathname>
Discover Info.plist files by build settings of the application target
124 125 126 127 128 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 124 def info_plist_paths @info_plist_paths ||= target.build_configurations.map do |config| config.build_settings['INFOPLIST_FILE'] end.compact.map { |file| Pathname(file) }.flatten.to_set end |
#keys_by_file(path) ⇒ Hash{String => Hash}
Read a file and collect all its keys
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 230 def keys_by_file(path) begin # Load strings file contents strings = read_strings_file(path) # Reject generated identifiers used by Interface Builder strings.reject! { |key, _| /^[a-zA-Z0-9]{3}-[a-zA-Z0-9]{2,3}-[a-zA-Z0-9]{3}/.match(key) } keys = Hash[strings.map do |key, value| [key, { value: key, comment: value.gsub(/[\r\n]/, ' ') }] end] log 'Found %s keys in file %s', keys.count, path keys rescue ArgumentError => error raise ArgumentError, 'Error while reading %s: %s' % [path, error] end end |
#languages ⇒ Set<String>
Find preferred languages, which is:
- either only the default_language, if specified
- or the intersection of native development and used languages
- or all used languages
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 103 def languages if default_language != nil # Use specified default language as primary language [default_language] else # Calculate the intersection of native development and used languages, # fallback to the latter only, if it is empty languages = native_dev_languages & used_languages if languages.empty? used_languages else languages end end end |
#native_dev_languages ⇒ Set<String>
Find the native development languages by trying to use the “Localization native development region” from Info.plist
154 155 156 157 158 159 160 161 162 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 154 def native_dev_languages @native_dev_languages ||= absolute_info_plist_paths.map do |path| begin read_plist_key(path, :CFBundleDevelopmentRegion) rescue ArgumentError => e warn e end end.compact.to_set end |
#read_plist_key(path, key) ⇒ String
Extracts a given key from a plist file given as a path
174 175 176 177 178 179 180 181 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 174 def read_plist_key(path, key) raise ArgumentError, "File '#{path}' doesn't exist" unless path.exist? raise ArgumentError, 'Path is required, but nil' if path.nil? raise ArgumentError, 'Key is required, but nil' if key.nil? out = `/usr/libexec/PlistBuddy -c "Print :#{key}" "#{path}" 2>&1`.chomp raise ArgumentError, "Error reading plist: #{out}" unless $?.success? out end |
#read_strings_file(path) ⇒ Hash
Read a .strings file given as a path
190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 190 def read_strings_file(path) raise ArgumentError, "File '#{path}' doesn't exist" unless path.exist? raise ArgumentError, "File '#{path}' is not a file" unless path.file? error = `plutil -lint -s "#{path}" 2>&1` raise ArgumentError, "File %s is malformed:\n#{error}" % path.to_s unless $?.success? json_or_error = `plutil -convert json "#{path}" -o -`.chomp raise ArgumentError, "File %s couldn't be converted to JSON.\n#{json_or_error}" % path.to_s unless $?.success? JSON.parse(json_or_error.force_encoding('UTF-8')) rescue EncodingError => e raise StandardError, "Encoding error in #{path}: #{e}" end |
#selected_strings_file_refs ⇒ Array<PBXFileReference>
Select strings files by language
74 75 76 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 74 def selected_strings_file_refs @selected_strings_file_refs ||= strings_file_refs.select { |file_ref| languages.include? file_ref.name } end |
#strings_file_refs ⇒ Array<PBXFileReference>
Discover all references to .strings files in project (e.g. Localizable.strings)
66 67 68 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 66 def strings_file_refs @strings_file_refs ||= find_file_refs_by_extname '.strings' end |
#used_languages ⇒ Set<String>
All used languages in the project
92 93 94 |
# File 'lib/xcres/analyzer/strings_analyzer.rb', line 92 def used_languages @used_languages ||= derive_used_languages(strings_file_refs) end |