diff --git a/src/dart.jl b/src/dart.jl new file mode 100644 index 0000000..26f1d37 --- /dev/null +++ b/src/dart.jl @@ -0,0 +1,180 @@ +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 + diff --git a/src/pot.jl b/src/pot.jl new file mode 100644 index 0000000..2441782 --- /dev/null +++ b/src/pot.jl @@ -0,0 +1,42 @@ +# Pot import/export functions + +is_comment(line; char = r"#") = startswith(line, char) + +function readpot(path, lang, name = "base") + lines = readlines(joinpath(path, lang, "LC_MESSAGES", string(name, ".pot"))) + filter!(x -> !is_comment(x, char = r"#(?!:)"), lines) + filter!(!isempty, lines) + lines = lines[3:end] + @assert length(lines) % 3 == 0 + output = [] + for l in 1:3:length(lines) + ref = split(lines[l], "#: ") + msgid = split(lines[l+1], "msgid ") + msgstr = split(lines[l+2], "msgstr ") + @assert length(ref) == 2 + @assert length(msgid) == 2 + @assert length(msgstr) == 2 + id = split(msgid[2], '"') + str = split(msgstr[2], '"') + if length(id) != 3 && l != 1 + @warn "Problems with $msgstr $msgid" + continue + end + if isempty(str[2]) + continue + end + push!(output, (lang, id[2], str[2], split(ref[2], ":")[1])) + end + output +end + +function pot_to_jl(lang, id) + """@translate "$lang" "$(id[2])" "$(str[2])" """ +end + +function pot_to_jl(srcpath, destpath, filename, basename) + langs = readdir(srcpath) + content = join([join(readpot(srcpath, lang, basename), "\n") + for lang in langs], "\n\n") + write(joinpath(destpath, string(filename, ".jl")), content) +end diff --git a/src/types.jl b/src/types.jl new file mode 100644 index 0000000..2019819 --- /dev/null +++ b/src/types.jl @@ -0,0 +1,6 @@ +struct Translatable + string::AbastractString + file::String + line::Int + +end