Skip to content
This repository has been archived by the owner on Jun 4, 2022. It is now read-only.

Commit

Permalink
Don't bother with plural translations in templates until I need to.
Browse files Browse the repository at this point in the history
  • Loading branch information
Adrian Cochrane committed Jan 3, 2018
1 parent 0b96c98 commit 6eb8ac3
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 150 deletions.
2 changes: 0 additions & 2 deletions data/page-l10n/cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
{% plural-form range=3 0 if count == 1 else (1 if count >= 2 and count <= 4 else 2) %}

{% msg %} NOT FOUND {% trans %}{% endmsg %}
{% msg %} Maybe you meant one of these other internal pages? {% trans %}{% endmsg %}
{% msg %} Could Not Connect To The Internet {% trans %}{% endmsg %}
Expand Down
54 changes: 0 additions & 54 deletions src/Services/Prosody/expression.vala
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,6 @@ namespace Odysseus.Templating.Expression {
token = new NotEqual();
else if (packed == 0x696E) /* "in" */
token = new In();
else if (packed == 0x25) /* "%" */
token = new Modulo();
else if (packed == 0x6966) /* "if" */
token = new Ternary();
else if (packed == 0x656C7365) /* "else" */
token = new TernaryElse();
else if (packed == 0x28) /* "(" */
token = new Parenthesized();
else if (packed == 0x29) /* ")" */
token = new CloseParen();
else
token = new Value(arg);
}
Expand Down Expand Up @@ -313,48 +303,4 @@ namespace Odysseus.Templating.Expression {
else return val.to_double();
}
}

/* The following are largely only useful for use in plural forms, but other templates may use it */
private class Modulo : Infix {
public override int lbp {get {return 120;}}
public override string name {get {return "%";}}
public override double eval() {
var X = (int) x, Y = (int) y;
return X % Y;
}
}

private class Ternary : Expression {
public override int lbp {get {return 5;}}
public override string name {get {return "if";}}
public override Expression led(Expression left) throws SyntaxError {
right = parser.expression(lbp);
if (right.name != "else")
throw new SyntaxError.INVALID_ARGS("Expected 'else' operator not found");
var cond = right.left;
right.left = left;
left = cond;
return this;
}

public override double eval() {return a ? right.x : right.y;}
}
private class TernaryElse : Infix {
public override int lbp {get {return 6;}}
public override string name {get {return "else";}}
}

private class Parenthesized : Expression {
public override int lbp {get {return 150;}}
public override string name {get {return "(";}}
public override Expression nud() throws SyntaxError {
var expr = parser.expression();
parser.advance(")");
return expr;
}
}
private class CloseParen : Expression {
public override int lbp {get {return 0;}}
public override string name {get {return ")";}}
}
}
125 changes: 31 additions & 94 deletions src/Services/Prosody/i18n.vala
Original file line number Diff line number Diff line change
Expand Up @@ -49,26 +49,6 @@ namespace Odysseus.Templating.Std.I18n {
throw new SyntaxError.OTHER("No catalogue file found for specified languages");
}

private uint8 parse_plural_form(Parser cat) throws SyntaxError {
WordIter plural_form;
cat.scan_until("plural-form", out plural_form);
if (plural_form == null || !ByteUtils.equals_str(plural_form.next(), "plural-form"))
throw new SyntaxError.INVALID_ARGS("Missing {%% plural-form %%} tag from catalogue");

var range_arg = ByteUtils.to_string(plural_form.next());
if (!range_arg.has_prefix("range="))
throw new SyntaxError.INVALID_ARGS("First argument to {%% plural-form %%} " +
"MUST be prefixed with `range=`!");
var range = int.parse(range_arg["range=".length:range_arg.length]);
if (range > 0 && range < 100)
throw new SyntaxError.INVALID_ARGS("`range` must be set to a number in range " +
"0-100, got %i!", range);

if (plural_formula != null)
plural_formula = new Expression.Parser(plural_form).expression();

return (uint8) range;
}
private Bytes locate_message(Parser cat, Bytes key) throws SyntaxError {
WordIter msg;
cat.scan_until("msg", out msg);
Expand Down Expand Up @@ -97,119 +77,76 @@ namespace Odysseus.Templating.Std.I18n {
ByteUtils.to_string(key));
}

private Template[] parse_translations(Parser cat, uint8 range) throws SyntaxError {
var translations = new Template[range];
for (var i = 0; i < range; i++) {
WordIter trans;
translations[i] = cat.parse("trans endmsg", out trans);
if (trans == null)
throw new SyntaxError.UNBALANCED_TAGS("Missing {%% endtrans %%}!");
if (ByteUtils.equals_str(trans.next(), i + 1 == range ? "endmsg" : "trans"))
throw new SyntaxError.UNBALANCED_TAGS("Incorrect number of translations!");
}
private Template parse_translations(Parser cat) throws SyntaxError {
// Essentially this is just a cat.parse call with extra verification.

WordIter endtrans;
var ret = cat.parse("endmsg", out endtrans);
if (endtrans == null)
throw new SyntaxError.UNBALANCED_TAGS("Missing {%% endtrans %%}!");
endtrans.next(); endtrans.assert_end();

return translations;
return ret;
}

// cache used to avoid keeping multiple copies of a translation in memory.
private class CacheEntry {
public Bytes key;
public weak Template[] translation;
public weak Template translation;
public CacheEntry? next;

public static List<CacheEntry> translation_cache = new List<CacheEntry>();
public static CacheEntry translation_cache = new CacheEntry();
}

private void lookup_translation(Bytes key,
ref Template[] bodies, ref Expression.Expression formula) {
foreach (var entry in CacheEntry.translation_cache) {
private void lookup_translation(Bytes key, ref Template body) {
// Look it up in the cache of translations already in memory...
CacheEntry? prev = null;
CacheEntry? entry = CacheEntry.translation_cache;
for (; entry != null; prev = entry, entry = entry.next) {
if (entry.translation == null) {
// Do a bit of cleanup
CacheEntry.translation_cache.remove(entry);
if (prev == null) CacheEntry.translation_cache = entry.next;
else prev.next = entry.next;
continue;
}

if (entry.key.compare(key) != 0) continue;

bodies = entry.translation;
// This is non-null, as a previous parse would have had to populate it
// before we had a chance to insert this cache.
formula = plural_formula;
body = entry.translation;
}

var is_singular = bodies.length == 1;
// Failing that, scan the catalog file.
try {
var cat = new Parser(load_catalogue());
var range = parse_plural_form(cat);
var key2 = locate_message(cat, key);
bodies = parse_translations(cat, is_singular ? 1 : range);
formula = plural_formula;
body = parse_translations(cat);

var entry = new CacheEntry();
entry = new CacheEntry();
// Matching key from catalogue,
// So the calling template needn't be kept in memory.
entry.key = key2;
entry.translation = bodies;
CacheEntry.translation_cache.prepend(entry);
entry.translation = body;
entry.next = CacheEntry.translation_cache;
CacheEntry.translation_cache = entry;
} catch (Error e) {
warning("Failed to parse translation catalogue: %s", e.message);
}
}

private Expression.Expression? plural_formula = null;
private Expression.Expression? english_formula = null;

public class TransBuilder : TagBuilder, Object {
public Template? build(Parser parser, WordIter args) throws SyntaxError {
var parameters = parse_params(args);

WordIter? endtoken;
Bytes key;
var bodies = parse_fallback(parser, out endtoken, out key);
var formula = english_formula;
var body = parser.parse("endtrans", out endtoken, out key);
if (endtoken == null)
throw new SyntaxError.UNBALANCED_TAGS("Missing {%% endtrans %%} tag.");
endtoken.assert_end();

lookup_translation(ByteUtils.strip(key), ref bodies, ref formula);

if (bodies.length > 1) {
if (!parameters.has_key(b("count")))
throw new SyntaxError.INVALID_ARGS("Plural {%% trans %%} tag" +
"MUST specify a `count` parameter.");

return new PluralTransTag(bodies, parameters, formula);
}
return new WithTag(parameters, bodies[0]);
}

private Template[] parse_fallback(Parser parser, out WordIter endtoken,
out Bytes key) throws SyntaxError {
var body = parser.parse("plural endtrans", out endtoken, out key);
if (endtoken != null && ByteUtils.equals_str(endtoken.next(), "plural")) {
var plural = parser.parse("endtrans", out endtoken);
if (english_formula == null) {
var english = smart_split(b("count != 1"), " ");
english_formula = new Expression.Parser(english).expression();
}
return new Template[2] {body, plural};
}
return new Template[1] {body};
}
}
private class PluralTransTag : Template {
private Template[] bodies;
private Gee.Map<Bytes,Variable> vars;
private Expression.Expression formula;

public PluralTransTag(Template[] bodies, Gee.Map<Bytes,Variable> vars,
Expression.Expression formula) {
this.bodies = bodies;
this.vars = vars;
this.formula = formula;
}
endtoken.next(); endtoken.assert_end();

public override async void exec(Data.Data ctx, Writer output) {
var i = (uint) formula.eval_type(Expression.TypePreference.NUMBER, ctx);
yield bodies[i].exec(new Data.Lazy(vars, ctx), output);
lookup_translation(ByteUtils.strip(key), ref body);
return new WithTag(parameters, body);
}
}
}

0 comments on commit 6eb8ac3

Please sign in to comment.