From b0982f74a1b247544aca1baf63952c78f0fbb9b5 Mon Sep 17 00:00:00 2001 From: Jared White Date: Fri, 8 Oct 2021 18:38:17 -0700 Subject: [PATCH 1/4] Process filename-based locale content and multi-locale output generation --- .../lib/bridgetown-core/collection.rb | 18 +++++++++++-- .../lib/bridgetown-core/configuration.rb | 13 +++++++--- .../lib/bridgetown-core/resource/base.rb | 25 +++++++++++++++++++ .../resource/permalink_processor.rb | 22 ++++++++++------ .../lib/bridgetown-core/static_file.rb | 11 ++++---- .../test/resources/src/_pages/multi-page.md | 9 +++++++ .../src/_pages/second-level-page.fr.md | 7 ++++++ .../resources/src/_pages/second-level-page.md | 4 ++- 8 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 bridgetown-core/test/resources/src/_pages/multi-page.md create mode 100644 bridgetown-core/test/resources/src/_pages/second-level-page.fr.md diff --git a/bridgetown-core/lib/bridgetown-core/collection.rb b/bridgetown-core/lib/bridgetown-core/collection.rb index 13972e5e9..98b880fba 100644 --- a/bridgetown-core/lib/bridgetown-core/collection.rb +++ b/bridgetown-core/lib/bridgetown-core/collection.rb @@ -304,8 +304,22 @@ def merge_environment_specific_metadata(data_contents) def read_resource(full_path) id = "repo://#{label}.collection/" + Addressable::URI.escape( Pathname(full_path).relative_path_from(Pathname(site.source)).to_s - ) - resource = Bridgetown::Model::Base.find(id).to_resource.read! + ).gsub("#", "%23") + model = Bridgetown::Model::Base.find(id) + + if model.attributes.key?(:locale) && model.locale.to_sym == :multi + site.config.available_locales.each do |locale| + model.locale = locale + add_model_resource model + end + return + end + + add_model_resource model + end + + def add_model_resource(model) + resource = model.to_resource.read! resources << resource if site.config.unpublished || resource.published? end diff --git a/bridgetown-core/lib/bridgetown-core/configuration.rb b/bridgetown-core/lib/bridgetown-core/configuration.rb index daac6cb50..2e7934ef2 100644 --- a/bridgetown-core/lib/bridgetown-core/configuration.rb +++ b/bridgetown-core/lib/bridgetown-core/configuration.rb @@ -55,8 +55,8 @@ class Configuration < HashWithDotAccess::Hash "show_dir_listing" => false, # Output Configuration - "available_locales" => ["en"], - "default_locale" => "en", + "available_locales" => [:en], + "default_locale" => :en, "permalink" => nil, # default is set according to content engine "timezone" => nil, # use the local timezone @@ -100,6 +100,7 @@ class << self def from(user_config, starting_defaults = DEFAULTS) Utils.deep_merge_hashes(starting_defaults.deep_dup, Configuration[user_config]) .merge_environment_specific_options! + .setup_locales .add_default_collections .add_default_excludes .check_include_exclude @@ -244,6 +245,12 @@ def merge_environment_specific_options! self end + def setup_locales + self.default_locale = default_locale.to_sym + available_locales.map!(&:to_sym) + self + end + def add_default_collections # rubocop:todo all # It defaults to `{}`, so this is only if someone sets it to null manually. return self if self[:collections].nil? @@ -264,7 +271,7 @@ def add_default_collections # rubocop:todo all self[:permalink] = "pretty" if self[:permalink].blank? self[:collections][:pages] = {} unless self[:collections][:pages] self[:collections][:pages][:output] = true - self[:collections][:pages][:permalink] ||= "/:path/" + self[:collections][:pages][:permalink] ||= "/:locale/:path/" self[:collections][:data] = {} unless self[:collections][:data] self[:collections][:data][:output] = false diff --git a/bridgetown-core/lib/bridgetown-core/resource/base.rb b/bridgetown-core/lib/bridgetown-core/resource/base.rb index 6c75cddf4..4520bf80b 100644 --- a/bridgetown-core/lib/bridgetown-core/resource/base.rb +++ b/bridgetown-core/lib/bridgetown-core/resource/base.rb @@ -295,6 +295,8 @@ def previous_resource private def ensure_default_data + determine_locale + slug = if matches = relative_path.to_s.match(DATE_FILENAME_MATCHER) # rubocop:disable Lint/AssignmentInCondition set_date_from_string(matches[1]) unless data.date matches[2] @@ -302,6 +304,8 @@ def ensure_default_data basename_without_ext end + slug.chomp!(".#{data.locale}") if data.locale && slug.ends_with?(".#{data.locale}") + data.slug ||= slug data.title ||= Bridgetown::Utils.titleize_slug(slug) end @@ -334,6 +338,27 @@ def import_taxonomies_from_data end end + def determine_locale + unless data.locale + # if locale key isn't directly set, look for alternative front matter + # or look at the filename pattern: slug.locale.ext + alternative = data.language || data.lang || + basename_without_ext.split(".")[1..-1].last + + data.locale = if !alternative.nil? && site.config.available_locales.include?(alternative.to_sym) + alternative + else + site.config.default_locale + end + end + + if data.locale_overrides&.is_a?(Hash) && data.locale_overrides&.key?(data.locale) + data.locale_overrides[data.locale].each do |k, v| + data[k] = v + end + end + end + def format_url(url) url.to_s.sub(%r{index\.html?$}, "").sub(%r{\.html?$}, "") end diff --git a/bridgetown-core/lib/bridgetown-core/resource/permalink_processor.rb b/bridgetown-core/lib/bridgetown-core/resource/permalink_processor.rb index 2ae59793b..22802aa2f 100644 --- a/bridgetown-core/lib/bridgetown-core/resource/permalink_processor.rb +++ b/bridgetown-core/lib/bridgetown-core/resource/permalink_processor.rb @@ -66,13 +66,13 @@ def permalink_for_permalink_style(permalink_style) case permalink_style.to_sym when :pretty - "#{collection_prefix}/:categories/:year/:month/:day/:slug/" + "/:locale/#{collection_prefix}/:categories/:year/:month/:day/:slug/" when :pretty_ext, :date - "#{collection_prefix}/:categories/:year/:month/:day/:slug.*" + "/:locale/#{collection_prefix}/:categories/:year/:month/:day/:slug.*" when :simple - "#{collection_prefix}/:categories/:slug/" + "/:locale/#{collection_prefix}/:categories/:slug/" when :simple_ext - "#{collection_prefix}/:categories/:slug.*" + "/:locale/#{collection_prefix}/:categories/:slug.*" else permalink_style.to_s end @@ -108,7 +108,13 @@ def add_base_path(permalink) # @param resource [Bridgetown::Resource::Base] register_placeholder :path, ->(resource) do - { raw_value: resource.relative_path_basename_without_prefix } + { + raw_value: resource.relative_path_basename_without_prefix.tap do |path| + if resource.data.locale && path.ends_with?(".#{resource.data.locale}") + path.chomp!(".#{resource.data.locale}") + end + end, + } end # @param resource [Bridgetown::Resource::Base] @@ -128,8 +134,10 @@ def add_base_path(permalink) # @param resource [Bridgetown::Resource::Base] register_placeholder :locale, ->(resource) do - locale_data = resource.data.locale - resource.site.config.available_locales.include?(locale_data) ? locale_data : nil + next nil if resource.data.locale&.to_sym == resource.site.config.default_locale + + locale_data = resource.data.locale&.to_sym + resource.site.config.available_locales.include?(locale_data) ? locale_data.to_s : nil end register_placeholder :lang, placeholder_processors[:locale] diff --git a/bridgetown-core/lib/bridgetown-core/static_file.rb b/bridgetown-core/lib/bridgetown-core/static_file.rb index 5ebc9d173..54559ccae 100644 --- a/bridgetown-core/lib/bridgetown-core/static_file.rb +++ b/bridgetown-core/lib/bridgetown-core/static_file.rb @@ -21,10 +21,11 @@ def reset_cache # Initialize a new StaticFile. # - # site - The Site. - # base - The String path to the . - # dir - The String path between and the file. - # name - The String filename of the file. + # @param site [Bridgetown::Site] + # @param base [String] path to the . + # @param dir [String] path between and the file. + # @param name [String] filename of the file. + # @param collection [Bridgetown::Collection] optional collection the file is attached to def initialize(site, base, dir, name, collection = nil) # rubocop:disable Metrics/ParameterLists @site = site @base = base @@ -36,7 +37,7 @@ def initialize(site, base, dir, name, collection = nil) # rubocop:disable Metric @data = @site.frontmatter_defaults.all(relative_path, type).with_dot_access if site.uses_resource? && !data.permalink data.permalink = if collection && !collection.builtin? - "/:collection/:path.*" + collection.default_permalink.chomp("/").chomp(".*") + ".*" else "/:path.*" end diff --git a/bridgetown-core/test/resources/src/_pages/multi-page.md b/bridgetown-core/test/resources/src/_pages/multi-page.md new file mode 100644 index 000000000..ad8503dd1 --- /dev/null +++ b/bridgetown-core/test/resources/src/_pages/multi-page.md @@ -0,0 +1,9 @@ +--- +title: "Multi-locale page" +locale_overrides: + fr: + title: "Sur mesure" +locale: multi +--- + +{% if site.locale == "en" %}English:{% elsif site.locale == "fr" %}French:{% endif %} {{ resource.data.title }} diff --git a/bridgetown-core/test/resources/src/_pages/second-level-page.fr.md b/bridgetown-core/test/resources/src/_pages/second-level-page.fr.md new file mode 100644 index 000000000..b98e54eef --- /dev/null +++ b/bridgetown-core/test/resources/src/_pages/second-level-page.fr.md @@ -0,0 +1,7 @@ +~~~ruby +{ title: "I'm a Second Level Page in French" } +~~~ + +C'est **bien**. + +Locale: {{ resource.data.locale }} \ No newline at end of file diff --git a/bridgetown-core/test/resources/src/_pages/second-level-page.md b/bridgetown-core/test/resources/src/_pages/second-level-page.md index 2b4a69a84..22bc2a0ac 100644 --- a/bridgetown-core/test/resources/src/_pages/second-level-page.md +++ b/bridgetown-core/test/resources/src/_pages/second-level-page.md @@ -2,4 +2,6 @@ { title: "I'm a Second Level Page" } ~~~ -That's **nice**. \ No newline at end of file +That's **nice**. + +Locale: {{ resource.data.locale }} \ No newline at end of file From 660210688b26c99544ad1add13695232dfff3da7 Mon Sep 17 00:00:00 2001 From: Jared White Date: Fri, 8 Oct 2021 19:17:21 -0700 Subject: [PATCH 2/4] Add tests for locale features --- .../lib/bridgetown-core/configuration.rb | 2 +- .../lib/bridgetown-core/resource/base.rb | 23 ++++---- bridgetown-core/test/helper.rb | 16 ++++++ ...-level-page.md => second-level-page.en.md} | 0 bridgetown-core/test/test_locales.rb | 52 +++++++++++++++++++ bridgetown-core/test/test_resource.rb | 17 +----- 6 files changed, 80 insertions(+), 30 deletions(-) rename bridgetown-core/test/resources/src/_pages/{second-level-page.md => second-level-page.en.md} (100%) create mode 100644 bridgetown-core/test/test_locales.rb diff --git a/bridgetown-core/lib/bridgetown-core/configuration.rb b/bridgetown-core/lib/bridgetown-core/configuration.rb index 2e7934ef2..8bde2e73a 100644 --- a/bridgetown-core/lib/bridgetown-core/configuration.rb +++ b/bridgetown-core/lib/bridgetown-core/configuration.rb @@ -271,7 +271,7 @@ def add_default_collections # rubocop:todo all self[:permalink] = "pretty" if self[:permalink].blank? self[:collections][:pages] = {} unless self[:collections][:pages] self[:collections][:pages][:output] = true - self[:collections][:pages][:permalink] ||= "/:locale/:path/" + self[:collections][:pages][:permalink] ||= "/:locale/:path/" self[:collections][:data] = {} unless self[:collections][:data] self[:collections][:data][:output] = false diff --git a/bridgetown-core/lib/bridgetown-core/resource/base.rb b/bridgetown-core/lib/bridgetown-core/resource/base.rb index 4520bf80b..0ba7f3d08 100644 --- a/bridgetown-core/lib/bridgetown-core/resource/base.rb +++ b/bridgetown-core/lib/bridgetown-core/resource/base.rb @@ -340,25 +340,22 @@ def import_taxonomies_from_data def determine_locale unless data.locale - # if locale key isn't directly set, look for alternative front matter - # or look at the filename pattern: slug.locale.ext - alternative = data.language || data.lang || - basename_without_ext.split(".")[1..-1].last - - data.locale = if !alternative.nil? && site.config.available_locales.include?(alternative.to_sym) - alternative - else - site.config.default_locale - end + data.locale = locale_from_alt_data_or_filename.presence || site.config.default_locale end if data.locale_overrides&.is_a?(Hash) && data.locale_overrides&.key?(data.locale) - data.locale_overrides[data.locale].each do |k, v| - data[k] = v - end + data.merge!(data.locale_overrides[data.locale]) end end + # Look for alternative front matter or look at the filename pattern: slug.locale.ext + def locale_from_alt_data_or_filename + found_locale = data.language || data.lang || basename_without_ext.split(".")[1..-1].last + return unless found_locale && site.config.available_locales.include?(found_locale.to_sym) + + found_locale + end + def format_url(url) url.to_s.sub(%r{index\.html?$}, "").sub(%r{\.html?$}, "") end diff --git a/bridgetown-core/test/helper.rb b/bridgetown-core/test/helper.rb index 88fb164fb..1c67aec74 100644 --- a/bridgetown-core/test/helper.rb +++ b/bridgetown-core/test/helper.rb @@ -149,6 +149,7 @@ def fixture_site(overrides = {}) def resources_site(overrides = {}) overrides["content_engine"] = "resource" + overrides["available_locales"] ||= %w[en fr] new_config = site_configuration(overrides) new_config.root_dir = resources_root_dir new_config.source = resources_root_dir("src") @@ -231,6 +232,21 @@ class FakeLogger def <<(str); end end +# stub +module Bridgetown + module Paginate + class PaginationIndexer + def self.index_documents_by(pages_list, search_term) + # site.collections[@configured_collection].resources + + pages_list.map do |resource| + [resource.data[search_term], nil] + end.to_h + end + end + end +end + module TestWEBrick module_function diff --git a/bridgetown-core/test/resources/src/_pages/second-level-page.md b/bridgetown-core/test/resources/src/_pages/second-level-page.en.md similarity index 100% rename from bridgetown-core/test/resources/src/_pages/second-level-page.md rename to bridgetown-core/test/resources/src/_pages/second-level-page.en.md diff --git a/bridgetown-core/test/test_locales.rb b/bridgetown-core/test/test_locales.rb new file mode 100644 index 000000000..c4edd5aa0 --- /dev/null +++ b/bridgetown-core/test/test_locales.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require "helper" + +class TestLocales < BridgetownUnitTest + context "pages in multiple locales" do + setup do + @site = resources_site + @site.process + # @type [Bridgetown::Resource::Base] + @english_resource = @site.collections.pages.resources.find do |page| + page.relative_path.to_s == "_pages/second-level-page.en.md" + end + @french_resource = @site.collections.pages.resources.find do |page| + page.relative_path.to_s == "_pages/second-level-page.fr.md" + end + end + + should "have the correct permalink and locale" do + assert_equal "/second-level-page/", @english_resource.relative_url + assert_includes @english_resource.output, "

Locale: en

" + end + + should "have the correct permalink" do + assert_equal "/fr/second-level-page/", @french_resource.relative_url + assert_includes @french_resource.output, "

C’est bien.

\n\n

Locale: fr

" + end + end + + context "one page which is generated into multiple locales" do + setup do + @site = resources_site + @site.process + # @type [Bridgetown::Resource::Base] + @resources = @site.collections.pages.resources.select do |page| + page.relative_path.to_s == "_pages/multi-page.md" + end + @english_resource = @resources.find { |page| page.data.locale == :en } + @french_resource = @resources.find { |page| page.data.locale == :fr } + end + + should "have the correct permalink and locale" do + assert_equal "/multi-page/", @english_resource.relative_url + assert_includes @english_resource.output, "

English: Multi-locale page

" + end + + should "have the correct permalink" do + assert_equal "/fr/multi-page/", @french_resource.relative_url + assert_includes @french_resource.output, "

French: Sur mesure

" + end + end +end diff --git a/bridgetown-core/test/test_resource.rb b/bridgetown-core/test/test_resource.rb index 48e5eeeca..9dc977bcb 100644 --- a/bridgetown-core/test/test_resource.rb +++ b/bridgetown-core/test/test_resource.rb @@ -2,21 +2,6 @@ require "helper" -# stub -module Bridgetown - module Paginate - class PaginationIndexer - def self.index_documents_by(pages_list, search_term) - # site.collections[@configured_collection].resources - - pages_list.map do |resource| - [resource.data[search_term], nil] - end.to_h - end - end - end -end - class TestResource < BridgetownUnitTest context "a top-level page" do setup do @@ -77,7 +62,7 @@ class TestResource < BridgetownUnitTest @site.process # @type [Bridgetown::Resource::Base] @resource = @site.collections.pages.resources.find do |page| - page.relative_path.to_s == "_pages/second-level-page.md" + page.relative_path.to_s == "_pages/second-level-page.en.md" end end From 96ffa72fd394a0a128cec928c11eef1414f3ac95 Mon Sep 17 00:00:00 2001 From: Jared White Date: Fri, 8 Oct 2021 19:22:22 -0700 Subject: [PATCH 3/4] Fix test descriptions --- bridgetown-core/test/test_locales.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bridgetown-core/test/test_locales.rb b/bridgetown-core/test/test_locales.rb index c4edd5aa0..0bb2f8527 100644 --- a/bridgetown-core/test/test_locales.rb +++ b/bridgetown-core/test/test_locales.rb @@ -3,7 +3,7 @@ require "helper" class TestLocales < BridgetownUnitTest - context "pages in multiple locales" do + context "similar pages in different locales" do setup do @site = resources_site @site.process @@ -16,12 +16,12 @@ class TestLocales < BridgetownUnitTest end end - should "have the correct permalink and locale" do + should "have the correct permalink and locale in English" do assert_equal "/second-level-page/", @english_resource.relative_url assert_includes @english_resource.output, "

Locale: en

" end - should "have the correct permalink" do + should "have the correct permalink and locale in French" do assert_equal "/fr/second-level-page/", @french_resource.relative_url assert_includes @french_resource.output, "

C’est bien.

\n\n

Locale: fr

" end @@ -39,12 +39,12 @@ class TestLocales < BridgetownUnitTest @french_resource = @resources.find { |page| page.data.locale == :fr } end - should "have the correct permalink and locale" do + should "have the correct permalink and locale in English" do assert_equal "/multi-page/", @english_resource.relative_url assert_includes @english_resource.output, "

English: Multi-locale page

" end - should "have the correct permalink" do + should "have the correct permalink and locale in French" do assert_equal "/fr/multi-page/", @french_resource.relative_url assert_includes @french_resource.output, "

French: Sur mesure

" end From e7de47265d07cd90e5434d73bd875b398713c70c Mon Sep 17 00:00:00 2001 From: Jared White Date: Sat, 9 Oct 2021 11:24:15 -0700 Subject: [PATCH 4/4] Fix permalink feature tests --- bridgetown-core/features/permalinks.feature | 45 ++++++--------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/bridgetown-core/features/permalinks.feature b/bridgetown-core/features/permalinks.feature index 6312b6336..843f5c454 100644 --- a/bridgetown-core/features/permalinks.feature +++ b/bridgetown-core/features/permalinks.feature @@ -136,6 +136,7 @@ Feature: Fancy permalinks And I have a configuration file with: | key | value | | permalink | /:lang/:year/:month/:day/:slug/ | + | content_engine | resource | | available_locales | [en, es] | When I run bridgetown build Then I should get a zero exit status @@ -154,11 +155,12 @@ Feature: Fancy permalinks And I have a configuration file with: | key | value | | permalink | /:lang/:year/:month/:day/:title/ | + | content_engine | resource | | available_locales | [en, es] | When I run bridgetown build Then I should get a zero exit status And the output directory should exist - And I should see "Impresionante!" in "output/es/2009/03/27/multi-lingual/index.html" + And I should see "Impresionante!" in "output/es/2009/03/27/custom-locale/index.html" Scenario: Don't use language permalink if locales aren't configured Given I have a _posts directory @@ -170,39 +172,13 @@ Feature: Fancy permalinks Impresionante! """ And I have a configuration file with: - | key | value | - | permalink | /:lang/:year/:month/:day/:slug/ | - When I run bridgetown build - Then I should get a zero exit status - And the output directory should exist - And I should see "Impresionante!" in "output/2009/03/27/not-multi-lingual-es/index.html" - - Scenario: Use custom permalink schema with multiple languages - Given I have a _posts directory - And I have an "_posts/2009-03-27-multi-lingual.en.md" file with content: - """ - --- - title: English Locale - --- - Awesome! {{ site.locale }} - """ - And I have an "_posts/2009-03-27-multi-lingual.es.md" file with content: - """ - --- - title: Custom Locale - language: es - --- - Impresionante! {{ site.locale }} - """ - And I have a configuration file with: - | key | value | - | permalink | /:lang/:year/:month/:day/:title/ | - | available_locales | [en, es] | + | key | value | + | permalink | /:lang/:year/:month/:day/:slug/ | + | content_engine | resource | When I run bridgetown build Then I should get a zero exit status And the output directory should exist - And I should see "Awesome! en" in "output/en/2009/03/27/multi-lingual/index.html" - And I should see "Impresionante! es" in "output/es/2009/03/27/multi-lingual/index.html" + And I should see "Impresionante!" in "output/2009/03/27/not-multi-lingual.es/index.html" Scenario: Use custom permalink schema with multiple languages and a default path Given I have a _posts directory @@ -222,7 +198,8 @@ Feature: Fancy permalinks """ And I have a configuration file with: | key | value | - | permalink | /:locale/:year/:month/:day/:title/ | + | permalink | /:locale/:year/:month/:day/:slug/ | + | content_engine | resource | | available_locales | [en, es] | When I run bridgetown build Then I should get a zero exit status @@ -249,7 +226,8 @@ Feature: Fancy permalinks """ And I have a configuration file with: | key | value | - | collections | {blogs: {output: true, permalink: "/:locale/:collection/:title/"}} | + | collections | {blogs: {output: true, permalink: "/:locale/:collection/:slug/"}} | + | content_engine | resource | | available_locales | [en, es] | When I run bridgetown build Then I should get a zero exit status @@ -270,6 +248,7 @@ Feature: Fancy permalinks And I have a configuration file with: | key | value | | permalink | /:lang/:year/:month/:day/:slug/ | + | content_engine | resource | | available_locales | [en, es] | When I run bridgetown build Then I should get a zero exit status