-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add `localize` template tag * Fix tag spec * Improve test description * Fix findings * Add documentation * Update docs/docs/templates/reference/tags.md * Update docs/docs/templates/reference/tags.md * Update docs/docs/templates/reference/tags.md * Update docs/docs/templates/reference/tags.md --------- Co-authored-by: Morgan Aubert <[email protected]>
- Loading branch information
Showing
8 changed files
with
304 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
require "./spec_helper" | ||
|
||
describe Marten::Template::Tag::Localize do | ||
describe "::new" do | ||
it "raises if the localize tag does not contain at least one argument" do | ||
parser = Marten::Template::Parser.new("") | ||
|
||
expect_raises( | ||
Marten::Template::Errors::InvalidSyntax, | ||
"Malformed localize tag: at least one argument must be provided" | ||
) do | ||
Marten::Template::Tag::Localize.new(parser, "localize") | ||
end | ||
end | ||
end | ||
|
||
describe "#render" do | ||
fixed_date_time = Time.utc(2024, 11, 19, 12, 45) | ||
fixed_date_time_default_format = "Tue, 19 Nov 2024 12:45:00 +0000" # "%a, %d %b %Y %H:%M:%S %z" | ||
fixed_date_time_long_format = "November 19, 2024 12:45" # "%B %d, %Y %H:%M" | ||
fixed_date_time_short_format = "19 Nov 12:45" # "%d %b %H:%M" | ||
fixed_date_default_format = "2024-11-19" # "%Y-%m-%d" | ||
fixed_date_long_format = "November 19, 2024" # "%B %d, %Y" | ||
fixed_date_short_format = "Nov 19" # "%b %d" | ||
|
||
it "is able to localize a simple numeric value" do | ||
parser = Marten::Template::Parser.new("") | ||
tag = Marten::Template::Tag::Localize.new(parser, %{localize 100000}) | ||
tag.render(Marten::Template::Context.new).should eq "100,000" # Default number format | ||
end | ||
|
||
it "is able to localize a time value" do | ||
parser = Marten::Template::Parser.new("") | ||
tag = Marten::Template::Tag::Localize.new(parser, %{localize date}) | ||
tag.render(Marten::Template::Context{"date" => fixed_date_time}).should eq fixed_date_time_default_format | ||
end | ||
|
||
it "is able to localize a date value" do | ||
parser = Marten::Template::Parser.new("") | ||
tag = Marten::Template::Tag::Localize.new(parser, %{localize date}) | ||
tag.render(Marten::Template::Context{"date" => fixed_date_time.date}).should eq fixed_date_default_format | ||
end | ||
|
||
it "is able to localize a time with a custom format" do | ||
parser = Marten::Template::Parser.new("") | ||
tag = Marten::Template::Tag::Localize.new(parser, %{localize date format: "short"}) | ||
tag.render(Marten::Template::Context{"date" => fixed_date_time}).should eq fixed_date_time_short_format | ||
end | ||
|
||
it "is able to localize a date with a custom format" do | ||
parser = Marten::Template::Parser.new("") | ||
tag = Marten::Template::Tag::Localize.new(parser, %{localize date format: "short"}) | ||
tag.render(Marten::Template::Context{"date" => fixed_date_time.date}).should eq fixed_date_short_format | ||
end | ||
|
||
it "is able to localize a time with a long format" do | ||
parser = Marten::Template::Parser.new("") | ||
tag = Marten::Template::Tag::Localize.new(parser, %{localize date format: "long"}) | ||
tag.render(Marten::Template::Context{"date" => fixed_date_time}).should eq fixed_date_time_long_format | ||
end | ||
|
||
it "is able to localize a date with a long format" do | ||
parser = Marten::Template::Parser.new("") | ||
tag = Marten::Template::Tag::Localize.new(parser, %{localize date format: "long"}) | ||
tag.render(Marten::Template::Context{"date" => fixed_date_time.date}).should eq fixed_date_long_format | ||
end | ||
|
||
it "is able to resolve values from the context for localization" do | ||
parser = Marten::Template::Parser.new("") | ||
context = Marten::Template::Context{"value" => 123456} | ||
|
||
tag = Marten::Template::Tag::Localize.new(parser, %{localize value}) | ||
tag.render(context).should eq "123,456" | ||
end | ||
|
||
it "is able to resolve the format from the context" do | ||
parser = Marten::Template::Parser.new("") | ||
context = Marten::Template::Context{"format" => "custom"} | ||
|
||
tag = Marten::Template::Tag::Localize.new(parser, %{localize 100000 format: format}) | ||
tag.render(context).should eq "10-00-00,00" | ||
end | ||
|
||
it "is able to assign the localized value to a specific variable" do | ||
parser = Marten::Template::Parser.new("") | ||
context = Marten::Template::Context{"date" => fixed_date_time} | ||
tag = Marten::Template::Tag::Localize.new(parser, %{localize date as localized_date}) | ||
|
||
tag.render(context).should eq "" | ||
context["localized_date"].to_s.should eq fixed_date_time_default_format | ||
end | ||
|
||
it "raises if a invalid date (too many fields) tuple is provided" do | ||
parser = Marten::Template::Parser.new("") | ||
tag = Marten::Template::Tag::Localize.new(parser, %{localize unsupported_date}) | ||
|
||
expect_raises( | ||
Marten::Template::Errors::UnsupportedValue, | ||
"Localization of dates requires an Array with exactly 3 elements, but received 4 elements." + | ||
" Ensure the Array follows the format [year, month, day]." | ||
) do | ||
tag.render(Marten::Template::Context{"unsupported_date" => {2024, 11, 19, 12}}) | ||
end | ||
end | ||
|
||
it "raises if a invalid date (types) tuple is provided" do | ||
parser = Marten::Template::Parser.new("") | ||
tag = Marten::Template::Tag::Localize.new(parser, %{localize unsupported_date}) | ||
|
||
expect_raises( | ||
Marten::Template::Errors::UnsupportedType, | ||
"Expected an Array with only Int32 elements, but found elements of types: String, Bool, Float64." | ||
) do | ||
tag.render(Marten::Template::Context{"unsupported_date" => {"2024", true, 1.0}}) | ||
end | ||
end | ||
|
||
it "raises if a non-supported type is provided" do | ||
parser = Marten::Template::Parser.new("") | ||
tag = Marten::Template::Tag::Localize.new(parser, %{localize unsupported_value}) | ||
|
||
expect_raises( | ||
Marten::Template::Errors::UnsupportedType, | ||
"The `localize` tag only supports localization of Time or numeric values, but got Nil" | ||
) do | ||
tag.render(Marten::Template::Context{"unsupported_value" => nil}) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
require "./concerns/*" | ||
|
||
module Marten | ||
module Template | ||
module Tag | ||
# The `localize` template tag. | ||
# | ||
# The `localize` template tag allows performing localization on values such as dates, numbers, and times | ||
# within templates. It can take one mandatory argument (the value to localize) and an optional `format` | ||
# keyword argument to specify a localization format. | ||
# | ||
# Usage examples: | ||
# ``` | ||
# {% localize created_at %} | ||
# {% localize 100000 format: "short" %} | ||
# ``` | ||
# | ||
# The `format` argument must match a key defined in the locale file. | ||
# | ||
# Optionally, the result of the localization can be assigned to a variable using `as` keyword: | ||
# | ||
# ``` | ||
# {% localize created_at format: "short" as localized_date %} | ||
# ``` | ||
class Localize < Base | ||
include CanExtractKwargs | ||
include CanSplitSmartly | ||
|
||
@assigned_to : String? = nil | ||
|
||
def initialize(parser : Parser, source : String) | ||
parts = split_smartly(source) | ||
|
||
if parts.size < 2 | ||
raise Errors::InvalidSyntax.new("Malformed localize tag: at least one argument must be provided") | ||
end | ||
|
||
@value_expression = FilterExpression.new(parts[1]) | ||
|
||
if parts.size > 2 && parts[-2] == "as" | ||
@assigned_to = parts[-1] | ||
kwargs_parts = parts[2...-2] | ||
else | ||
kwargs_parts = parts[2..] | ||
end | ||
|
||
@kwargs = {} of String => FilterExpression | ||
extract_kwargs(kwargs_parts.join(' ')).each do |key, value| | ||
@kwargs[key] = FilterExpression.new(value) | ||
end | ||
end | ||
|
||
def render(context : Context) : String | ||
value = @value_expression.resolve(context).raw | ||
|
||
if format_value = @kwargs["format"]? | ||
format = format_value.resolve(context).to_s | ||
end | ||
|
||
localized_value = localize_value(value, format) | ||
|
||
if @assigned_to.nil? | ||
localized_value | ||
else | ||
context[@assigned_to.not_nil!] = localized_value | ||
"" | ||
end | ||
end | ||
|
||
private def localize_value(value, format : String?) : String | ||
case value | ||
when Time, Float64, Int32, Int64 | ||
valid_value = value.as(Float64 | Int32 | Int64 | Time) | ||
format.nil? ? I18n.l(valid_value) : I18n.l(valid_value, format) | ||
when Array(Marten::Template::Value) | ||
if value.size != 3 | ||
raise Errors::UnsupportedValue.new( | ||
"Localization of dates requires an Array with exactly 3 elements, but received #{value.size}" + | ||
" elements. Ensure the Array follows the format [year, month, day]." | ||
) | ||
end | ||
|
||
if !value.all? { |element| element.raw.is_a?(Int32) } | ||
types = value.map(&.raw.class).uniq! | ||
raise Errors::UnsupportedType.new( | ||
"Expected an Array with only Int32 elements, but found elements of types: #{types.join(", ")}. " + | ||
"Please ensure all elements are Int32." | ||
) | ||
end | ||
date_value = {value[0].raw.as(Int32), value[1].raw.as(Int32), value[2].raw.as(Int32)} | ||
format.nil? ? I18n.l(date_value) : I18n.l(date_value, format) | ||
else | ||
raise Errors::UnsupportedType.new( | ||
"The `localize` tag only supports localization of Time or numeric values, but got #{value.class}" | ||
) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |