function translation_file(translations; interpolation = false) interpolate = "String fill(List params) => localizeFill(this, params);" """import 'package:i18n_extension/i18n_extension.dart'; extension Localization on String { static var _t = Translations.byLocale("en_us") + { $(translations) }; $(interpolation ? interpolate : "") String get i18n => localize(this, _t); } """ end placeholder(n; place = "STRING") = string(place, "#", string(n, pad=20)) function extract(regex, content) placeholders = Dict() for (idx, interp) in enumerate(eachmatch(regex, content)) holder = placeholder(idx, place = "INTERPOLATION") placeholders[holder] = interp.match end placeholders end function replace_extracted(content, placeholders) inv = Dict(map((p) -> p[2] => p[1], collect(placeholders))) # for placeholder in keys(placeholder) for key in keys(inv) content = replace(content, key => inv[key]) end content end function find_all_strings(basepath, filepath) content = read(joinpath(basepath, filepath), String) # String interpolation ${} can contain anything (including unescaped strings) # So it's better to extract it before doing anything # Replace them with identifiers placeholders = extract(r"(\$\{.*?\}|\$[A-Za-z][A-Za-z0-9]*)", content) content = replace_extracted(content, placeholders) # interpolations = replace(content, r"${.*}" => ) matches = eachmatch(r"""(?P'''|'|")(?P.+?)(?(?P\.i18n)?"""s, content) # long_lines = [ # placeholder(i, place = "LONGDESCRIPTION") => a for (i, a) in # filter(m -> occursin("\n", m.match), collect(matches)) |> enumerate # ] |> Dict # Now we need to find the lines of the matches. (placeholders = placeholders, matches = collect(matches), content = content) end # function find_all_strings(basepath, filepath; i18n = true) # content = read(joinpath(basepath, filepath), String) # # Remove comments # content = replace(content, r"""///?.*"""m => "") # content = replace(content, r"""/\*.*?\*/"""s => "") # # Remove imports/export # content = replace(content, r"""(import|export|part) .*"""m => "") # # Mark new lines to extract string positions # lines = findall(x -> x == '\n', collect(content)) # # Find all types of strings # # ? EXCLUDE PRINT? # strings = eachmatch(r"""(['"])(.*?)\1(\.i18n)?""", content) # out = [] # for string in strings # (i18n && isnothing(string[3])) && continue # # TODO: Interactively modify the source code to: # # 1. Add the .i18n # # 2. Include the file # # Get the line of the match # line = findfirst(line -> line >= string.offset, lines) # push!(out, (file = filepath, line = line, strid = string[2])) # end # out # end dartfiles(files) = filter(f -> endswith(f, ".dart"), files) function find_dart_files(path) out = [] for (root, dirs, files) in walkdir(path) for file in dartfiles(files) push!(out, joinpath(root, file)) end end out end function find_all_strings(path) no_i18n(files) = filter(f -> !endswith(f, "i18n.dart"), files) excluded(files) = filter(f -> !(f in exclude), files) path = realpath(path) files = find_dart_files(path) |> no_i18n find_all_strings.(Ref(path), files) end read_dart_project(path; exclude = []) = find_all_strings(path, exclude = exclude, i18n = true) function i18n_project(path) exc = ".i18n-excluded" ign = ".i18n-ignored" splitter = "\0ยค\0" ignored = isfile(ign) ? map(l -> split(l, splitter), readlines(ign)) : [] new_excludes = [] edits = [] adds = [] cd(path) do exclude = isfile(exc) ? readlines(exc) : [] strings = vcat(find_all_strings(path; exclude = exclude, i18n = false)...) files = map(s -> s.file, strings) |> unique @info "You have a total of $(length(strings)) strings in $(length(files)) files" @warn "!!!Remember to git commit before running this!!!" for file in files content = readlines(file) @info "Inspecting file $(file)?" for str in filter(s -> s.file == file, strings) if !isnothing(findfirst(i -> i[1] == str.file && i[2] == string(str.line) && i[3] == str.strid, ignored)) continue end println("Line $(str.line): >$(str.strid)< ") println(content[str.line]) println("[e]xclude, [a]dd, [c]hange, [i]gnore") resp = ' ' while !(resp in ["e", "a", "c", "i"]) resp = readline(stdin) end if resp == "e" push!(new_excludes, file) break elseif resp == "a" push!(adds, str) continue elseif resp == "c" push!(edits, str => readline(stdin)) elseif resp == "i" open(ign, "a+") do f println(f, join([str.file, str.line, str.strid], splitter)) end continue else @error "Unknown problem" end end end end ## Process changes @warn "Applying changes in 3s... last chance to interrupt (Ctrl-C)" sleep(3) open(exc, "a+") do f write(f, join(new_excludes, "\n")) end edits, adds end struct Translation lang::String msgstr::String end struct Translatable msgid::String translations::Array{Translation} file::Union{String,Nothing} line::Union{UInt,Nothing} end function dart_to_pot(path) files = find_all_strings(path) for file in files i18n_s = filter(!isnothing, map(m -> m["i18n"], file.matches)) length(i18n_s) == 0 && continue @show i18n_s end end