parent
f5148da326
commit
36b9bae647
@ -0,0 +1,180 @@ |
||||
function translation_file(translations; interpolation = false) |
||||
interpolate = "String fill(List<Object> 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<quote>'''|'|")(?P<string>.+?)(?<!\\)\k<quote>(?P<i18n>\.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 |
||||
|
@ -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 |
@ -0,0 +1,6 @@ |
||||
struct Translatable |
||||
string::AbastractString |
||||
file::String |
||||
line::Int |
||||
|
||||
end |
Loading…
Reference in new issue