From 4ed9d13803d93f88051aece69dc5613241dd87b3 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sun, 20 Oct 2024 21:35:27 +0300 Subject: [PATCH] veb: translations via %translation_key --- cmd/tools/vast/vast.v | 2 +- vlib/orm/orm_insert_test.v | 1 + vlib/v/ast/ast.v | 2 +- vlib/v/checker/comptime.v | 2 +- vlib/v/checker/errors.v | 2 +- vlib/v/gen/c/comptime.v | 4 +- vlib/v/markused/walker.v | 2 +- vlib/v/parser/comptime.v | 2 +- vlib/v/parser/parse_type.v | 2 +- vlib/v/parser/tests/map_init_void.out | 2 +- vlib/v/parser/tmpl.v | 11 ++-- vlib/veb/tr.v | 95 +++++++++++++++++++++++++++ vlib/veb/veb.v | 3 + 13 files changed, 115 insertions(+), 15 deletions(-) create mode 100644 vlib/veb/tr.v diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index 8d03e49963f6e9..0ed65fff131286 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -1028,7 +1028,7 @@ fn (t Tree) comptime_call(node ast.ComptimeCall) &Node { obj.add_terse('method_name', t.string_node(node.method_name)) obj.add_terse('left', t.expr(node.left)) obj.add_terse('is_vweb', t.bool_node(node.is_vweb)) - obj.add_terse('vweb_tmpl', t.string_node(node.vweb_tmpl.path)) + obj.add_terse('veb_tmpl', t.string_node(node.vweb_tmpl.path)) obj.add_terse('args_var', t.string_node(node.args_var)) obj.add_terse('has_parens', t.bool_node(node.has_parens)) obj.add_terse('is_embed', t.bool_node(node.is_embed)) diff --git a/vlib/orm/orm_insert_test.v b/vlib/orm/orm_insert_test.v index 9d8967265c94d6..063aac4a16a9b9 100644 --- a/vlib/orm/orm_insert_test.v +++ b/vlib/orm/orm_insert_test.v @@ -442,6 +442,7 @@ fn test_the_result_of_insert_should_be_the_last_insert_id() { insert address into Address } or { panic(err) } dump(aid1) + assert aid1 == 1 aid2 := sql db { insert address into Address } or { panic(err) } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 03e94d1ca37878..ca0830eda0b754 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1989,7 +1989,7 @@ pub: mut: is_d_resolved bool pub mut: - vweb_tmpl File + veb_tmpl File left Expr left_type Type result_type Type diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 9ce43873d9a7a8..fdd9cc772f5339 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -100,7 +100,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { } mut c2 := new_checker(c.table, pref2) c2.comptime_call_pos = node.pos.pos - c2.check(mut node.vweb_tmpl) + c2.check(mut node.veb_tmpl) c.warnings << c2.warnings c.errors << c2.errors c.notices << c2.notices diff --git a/vlib/v/checker/errors.v b/vlib/v/checker/errors.v index 92c85a3d7fa4f5..2d0df6d5a1115c 100644 --- a/vlib/v/checker/errors.v +++ b/vlib/v/checker/errors.v @@ -50,7 +50,7 @@ fn (mut c Checker) error(message string, pos token.Pos) { mut msg := message.replace('`Array_', '`[]') if c.pref.is_vweb { // Show in which veb action the error occurred (for easier debugging) - veb_action := c.table.cur_fn.name.replace('vweb_tmpl_', '') + veb_action := c.table.cur_fn.name.replace('veb_tmpl_', '') mut j := 0 for _, ch in veb_action { if ch.is_digit() { diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index d82aae9ba2944b..c62ac7573670f4 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -87,9 +87,9 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) { is_x_vweb := ret_sym.cname == 'x__vweb__Result' is_veb := ret_sym.cname == 'veb__Result' - for stmt in node.vweb_tmpl.stmts { + for stmt in node.veb_tmpl.stmts { if stmt is ast.FnDecl { - if stmt.name.starts_with('main.vweb_tmpl') { + if stmt.name.starts_with('main.veb_tmpl') { if is_html { g.inside_vweb_tmpl = true if is_veb { diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index accdb62c9b7c8a..8eefe1bfd631f8 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -286,7 +286,7 @@ fn (mut w Walker) expr(node_ ast.Expr) { ast.ComptimeCall { w.expr(node.left) if node.is_vweb { - w.stmts(node.vweb_tmpl.stmts) + w.stmts(node.veb_tmpl.stmts) } } ast.DumpExpr { diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 02ec191964b3d6..493f8fce975870 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -324,7 +324,7 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { scope: unsafe { nil } is_vweb: true is_veb: is_veb - vweb_tmpl: file + veb_tmpl: file method_name: method_name args_var: literal_string_param args: [arg] diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index a60ce7c64fc734..378751b78de956 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -176,7 +176,7 @@ fn (mut p Parser) parse_map_type() ast.Type { return 0 } if value_type.idx() == ast.void_type_idx { - p.error_with_pos('map value type cannot be void', p.tok.pos()) + p.error_with_pos('map value type is missing: use `map[KeyType]ValueType`', p.tok.pos()) return 0 } idx := p.table.find_or_register_map(key_type, value_type) diff --git a/vlib/v/parser/tests/map_init_void.out b/vlib/v/parser/tests/map_init_void.out index fd76d7ee0f0aba..b60477eba58536 100644 --- a/vlib/v/parser/tests/map_init_void.out +++ b/vlib/v/parser/tests/map_init_void.out @@ -1,4 +1,4 @@ -vlib/v/parser/tests/map_init_void.vv:2:18: error: map value type cannot be void +vlib/v/parser/tests/map_init_void.vv:2:18: error: map value type is missing: use `map[KeyType]ValueType` 1 | fn main() { 2 | m := map[string]{} | ^ diff --git a/vlib/v/parser/tmpl.v b/vlib/v/parser/tmpl.v index 650e07e5e3f6a2..cda24f627bc036 100644 --- a/vlib/v/parser/tmpl.v +++ b/vlib/v/parser/tmpl.v @@ -75,7 +75,7 @@ fn is_html_open_tag(name string, s string) bool { fn insert_template_code(fn_name string, tmpl_str_start string, line string) string { // HTML, may include `@var` - // escaped by cgen, unless it's a `vweb.RawHtml` string + // escaped by cgen, unless it's a `veb.RawHtml` string trailing_bs := tmpl_str_end + 'sb_${fn_name}.write_u8(92)\n' + tmpl_str_start replace_pairs := ['\\', '\\\\', r"'", "\\'", r'@@', r'@', r'@', r'$', r'$$', r'\@'] mut rline := line.replace_each(replace_pairs) @@ -225,8 +225,9 @@ pub fn (mut p Parser) compile_template_file(template_file string, fn_name string mut source := strings.new_builder(1000) source.writeln(' import strings -// === vweb html template === -fn vweb_tmpl_${fn_name}() string { +import veb +// === veb html template === +fn veb_tmpl_${fn_name}() string { mut sb_${fn_name} := strings.new_builder(${lstartlength})\n ') @@ -438,7 +439,7 @@ fn vweb_tmpl_${fn_name}() string { key := line[pos + 1..end] println('GOT tr key line="${line}" key="${key}"') // source.writeln('\${tr("${key}")}') - line_ = line.replace('%${key}', '\${tr("${key}")}') + line_ = line.replace('%${key}', '\${veb.tr(ctx.lang.str(), "${key}")}') // i += key.len } // println(source.str()) @@ -454,7 +455,7 @@ fn vweb_tmpl_${fn_name}() string { source.writeln('\t_tmpl_res_${fn_name} := sb_${fn_name}.str() ') source.writeln('\treturn _tmpl_res_${fn_name}') source.writeln('}') - source.writeln('// === end of vweb html template_file: ${template_file} ===') + source.writeln('// === end of veb html template_file: ${template_file} ===') result := source.str() $if trace_tmpl_expansion ? { diff --git a/vlib/veb/tr.v b/vlib/veb/tr.v new file mode 100644 index 00000000000000..8e409d4ae6c33e --- /dev/null +++ b/vlib/veb/tr.v @@ -0,0 +1,95 @@ +// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module veb + +import os + +const tr_map = load_tr_map() + +pub fn raw(s string) RawHtml { + return RawHtml(s) +} + +/* +struct TrData { + data +} + m map[string]TrData + */ + +// This function is run once, on app startup. Setting the `tr_map` const. +// m['en']['house'] == 'House' +fn load_tr_map() map[string]map[string]string { + // Find all translation files to figure out how many languages we have and to load the translation map + files := os.walk_ext('translations/', '.tr') + mut res := map[string]map[string]string{} + for tr_path in files { + lang := fetch_lang_from_tr_path(tr_path) + text := os.read_file(tr_path) or { + eprintln('translation file "${tr_path}" failed to laod') + return {} + } + x := text.split('-----\n') + for i, s in x { + // println('val="${val}"') + nl_pos := s.index('\n') or { continue } + key := s[..nl_pos] + val := s[nl_pos..] + // v := vals[i + 1] + // println('key="${key}" => val="${v}"') + res[lang][key] = val + // println(val) + } + } + return res +} + +fn fetch_lang_from_tr_path(path string) string { + return path.find_between('/', '.') +} + +// Used by %key in templates +pub fn tr(lang string, key string) string { + res := tr_map[lang][key] + if res == '' { + eprintln('NO TRANSLATION FOR KEY "${key}"') + return key + } + return RawHtml(res) +} + +pub fn tr_plural(lang string, key string, amount int) string { + s := tr_map[lang][key] + if s == '' { + eprintln('NO TRANSLATION FOR KEY "${key}"') + return key + } + if s.contains('|') { + //----- + // goods + // товар|а|ов + vals := s.split('|') + if vals.len != 3 { + return s + } + amount_str := amount.str() + // 1, 21, 121 товар + ending := if amount % 10 == 1 && !amount_str.ends_with('11') { // vals[0] + '' + // 2, 3, 4, 22 товара + } else if amount % 10 == 2 && !amount_str.ends_with('12') { + vals[1] + } else if amount % 10 == 3 && !amount_str.ends_with('13') { + vals[1] + } else if amount % 10 == 4 && !amount_str.ends_with('14') { + vals[1] + } else { + // 5 товаров, 11 товаров etc + vals[2] + } + return vals[0] + ending + } else { + return s + } +} diff --git a/vlib/veb/veb.v b/vlib/veb/veb.v index 5302794fb14848..b2e23c3041c2a8 100644 --- a/vlib/veb/veb.v +++ b/vlib/veb/veb.v @@ -1,3 +1,6 @@ +// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. module veb import io