Skip to content

Instantly share code, notes, and snippets.

@YusukeHosonuma
Created May 23, 2023 01:30
Show Gist options
  • Save YusukeHosonuma/0362b8787062cb72afb8e66ed7f208cb to your computer and use it in GitHub Desktop.
Save YusukeHosonuma/0362b8787062cb72afb8e66ed7f208cb to your computer and use it in GitHub Desktop.
Goodbye Localizable.strings with SwiftGen - SwiftGen によるローカライズを剥がすスクリプト
# -----------------------------------------------------------------------------
# SwiftGen によるローカライズを剥がすスクリプト。
# -----------------------------------------------------------------------------
class String
# シンボル変換
def symbol
# e.g.
# - OK → Ok
# - FAQ → Faq
if /^[A-Z]{2,}+$/ =~ self
self[0].upcase + self.downcase[1..]
else
self
end
end
# 先頭を小文字に
def property
self[0].downcase + self[1..self.length]
end
end
#
# Localizable.strings をパース。
#
def parse_localizable(path)
File.open(path, "r") do |f|
f.each_line do |line|
if /"(.+?)"\s+=\s+"(.+?)"/ =~ line
key = $1
val = $2
# 間違って `..` が含まれるキー定義があるので複数を考慮
ws = key.split(/\.+/)
ws = ws.map{ |w| w.symbol }
ws[-1] = ws[-1].property # 末尾の先頭は小文字に(e.g. `Text` → `text`)
key = 'L10n.' + ws.join('.')
$strings[key] = val
end
end
end
end
#
# Localizable.strings の値を取得する。
#
def lookup(key)
value = "#{$strings[key]}"
raise "Key '#{key}' is not found." if value.empty?
value
end
KEY = "L10n\.[0-9a-zA-Z.]+"
TOKENS = "0-9a-zA-Z.\/*\s:\"\$:"
EXPR = "[#{TOKENS}(]+[#{TOKENS})]+?"
#
# 複数の引数を持つ定義を置換。
#
def replace_multi_args(path)
text = File.open(path, "r") { |f| f.read }
pattern = /(#{KEY})\(\s*(#{EXPR})((,\s*#{EXPR})+)\s*\)/
if pattern =~ text
value = lookup($1)
params = [$2] + $3.split(/\s*,\s*/)[1..]
params.each do |param|
value.sub!('%@', "\\(#{param})")
end
text.sub!(pattern, %|"#{value}"|)
end
File.open(path, "w") { |f| f.write(text) }
replace_multi_args(path) if pattern =~ text
end
#
# 単数の引数、または引数なしの定義を置換。
#
def replace(path)
new_lines = []
patterns = [
/"\\\((#{KEY})\((#{EXPR})\)\)"/,
/(#{KEY})\(String\((#{EXPR})\)\)/,
/(#{KEY})\((#{EXPR})\)/,
/(#{KEY})\("\\\((#{EXPR})\)"\)/,
/(#{KEY})/,
]
File.open(path, "r") do |f|
f.each_line do |line|
if /\((#{KEY})\((#{EXPR})\)\)/ =~ line
match = Regexp.last_match[0]
value = lookup($1)
value.sub!('%@', "\\(#{$2})")
line.sub!(match, %|("#{value}")|)
elsif patterns.any? { |p| p =~ line }
match = Regexp.last_match[0]
value = lookup($1)
value.sub!('%@', "\\(#{$2})") if $2
line.sub!(match, %|"#{value}"|)
end
new_lines << line
end
end
text = new_lines.join
File.open(path, "w") { |f| f.write(text) }
replace(path) if /#{KEY}/ =~ text
end
#
# ローカライズ関連のファイルを削除。
#
def delete_files
['**/Localizable.strings', '**/Strings.swift'].each do |pattern|
Dir.glob(pattern) do |path|
File.delete(path)
end
end
end
# -----------------------------------------------------------------------------
# main
# -----------------------------------------------------------------------------
$strings = {}
#
# 1. Localizable.strings のパース
#
Dir.glob('**/Base.lproj/Localizable.strings') do |path|
parse_localizable(path)
end
#
# 2. 置換
#
Dir.glob('**/*.swift') do |file|
next if file.end_with?('Strings.swift')
replace_multi_args(file)
replace(file)
end
#
# 3. 不要になるファイルの削除
#
delete_files
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment