6

I know CSS isn't supposed to have content, but it does, like this nice box (below) extracted from the Twitter Bootstrap documentation CSS:

box screen shot

/* Echo out a label for the example */
.bs-docs-example:after {
  content: "Example";
}

I don't care for "Example", I use something like that as a mixin:

.box (@legend) {
  /* Echo out a label for the example */
  &:after {
    content: @legend;
  }
}

Then I don't need really dynamic CSS, I can easily include the mixin in a class, but instead of passing "Observation" I need to pass t 'box.observation':

.observation {
  .box("<%= t 'box.observation' =>");
}

Rails is supposed to follow Conventions over Configuration, it is very easy to just add a static CSS/LESS/SCSS and it is already included in all pages in a single minified CSS. What is the convention for internationalized CSS? For example, where I am supposed to put declarations like that of .observation?

michelpm
  • 1,795
  • 2
  • 18
  • 31
  • I believe your trouble will come from the fact that assets are precompiled down to one application.css. Internationalization, however, happens at run time. So you'd probably have to create your CSS dynamically for this to all work. – Jesse Wolgamott Apr 26 '13 at 21:20
  • The CSS's only change as much as the locales, I would think that creating it at run time would be overkill, don't you think? – michelpm Apr 26 '13 at 21:23
  • I like the answer here: http://stackoverflow.com/questions/9310044/rails-i18n-of-css-file. You extract out the CSS that is locale specific to CSS files with language abbreviations in their filenames. – mccannf Apr 26 '13 at 22:05
  • That is pretty neat, but not good enough for what I am asking. I would have to create several less/scss files for each locale and have all include the exact same content. I feel it is close, though. – michelpm Apr 26 '13 at 22:24
  • How many lines of CSS per language is there? – rovermicrover Apr 26 '13 at 22:30
  • At least as many CSS classes that have `content` attribute or similar, because there is no good reason for it to be in a "default language". In the box example, there would be at least 3 non-blank lines for each differently titled box, yet it is not really the point, there is no good reason for duplication here. – michelpm Apr 26 '13 at 22:36
  • 1
    Hmm. What about adding the locale to the HTML tag? See this question: http://stackoverflow.com/questions/11577083/access-the-locale-in-ruby-in-css-files – mccannf Apr 26 '13 at 23:02
  • Wow, that is also pretty neat, but I would have to generate a code just a little better than `.observation.en` or `.observation_en`. – michelpm Apr 26 '13 at 23:11
  • @mccannf I posted an answer (http://stackoverflow.com/a/16246891/619510), it started with an explanation why the answers you passed are not optimal for this problem and proposed one that is leaner, even though it does not work in my specific case without a few important caveats. – michelpm Apr 27 '13 at 22:49

2 Answers2

2

I could generate one CSS per locale as suggested in this answer, but since the CSS is the same except by the I18n bits, I would have either of:

  1. A folder with a multitude of static CSS/LESS with the locale inlined, e.g.:

    /* en */
    .observation {
      .box("Observation");
    }
    
  2. Lots of exactly identical dynamic CSS's, e.g.

    /* en */
    html[lang=en] {
      .observation {
        .box("Observation")
      }
    }
    

Instead I choose to create a CSS and ERB view and deliver using using page caching with the locale code in the URL, this way there is no duplication. See code below.

config/routes.rb

X::Application.routes.draw do
  get 'index.:locale.:format' => 'css#index',
    constraints: { locale: /[a-z]{2}(-[A-Z]{2})?/,
                   format: 'css' }
end

app/controllers/css_controller.rb

class CssController < ActionController::Base
  caches_page :index
  def index
    @locale = params[:locale]
  end
end

app/views/css/index.css.less.erb

@import "mixins";
.observation {
  .box("<%= t 'box.observation', locale: @locale %>");
}

app/assets/stylesheets/mixins.less

.box (@legend) {
  /* Echo out a label for the example */
  &:after {
    content: @legend;
  }
}

This example would work as is if it was a simple ERB view, but since it is using Less, I have to parse the ERB and LESS manually as of rails 4:

class CssController < ActionController::Base
  caches_page :index

  def index
    @locale = params[:locale]
    erb_source  = find_template.source
    less_source = Tilt::ERBTemplate.new { erb_source }.render(self)
    css_source  = Less::Parser.new(paths: Rails.application.config.less.paths).parse(less_source).to_css
    render text: css_source
  end

  private

  def find_template(_action_name = action_name)
    lookup_context.disable_cache { lookup_context.find_template(_action_name, lookup_context.prefixes) }
  end
end
Community
  • 1
  • 1
michelpm
  • 1,795
  • 2
  • 18
  • 31
2

You don't need to generate a new CSS file for each locale - that borders on madness. Why does your CSS care about the text content of your website?

I think your best bet would be to use a data-attribute to grab the value...

<div class='bs-docs-example' data-before-content='<%= t.css_example %>'>
  <!-- html here -->
</div>

And then in your css:

.bs-docs-example:after {
  content: attr(data-before-content);
}

You could probably find a way to extract this into a partial (or helper), so that your erb file ends up like this:

<%= docs_example do %>
  <!-- html here -->
<% end %>

And a helper method:

def docs_example
  content_tag(:div, class: "bs-docs-example", "data-before-content" => t.css_example) do
    yield
  end
end
nzifnab
  • 15,876
  • 3
  • 50
  • 65
  • This is amazing. Do you know how well supported is the 'attr' attribute? – michelpm May 01 '13 at 16:26
  • 1
    I believe it has the same support that the `content` CSS attribute itself has: http://caniuse.com/#feat=css-gencontent - So IE 8+, Firefox, Chrome, Safari. – nzifnab May 01 '13 at 16:33
  • I think I got the syntax of the translator wrong `t.css_example`, but you know how to do that part already :p – nzifnab May 01 '13 at 16:35