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 |