Skip to content

Instantly share code, notes, and snippets.

@w3core
Last active May 19, 2016 13:01
Show Gist options
  • Save w3core/1417a245ca42559d8cb9c45dc2740da6 to your computer and use it in GitHub Desktop.
Save w3core/1417a245ca42559d8cb9c45dc2740da6 to your computer and use it in GitHub Desktop.
Translates all textual parts of "content" rule in CSS stylesheet file. Accepts an optional `translate-values` interpolate parameters to pass dynamized values though translation.
/*!
* angular-translate - v2.10.0 - 2016-02-28
*
* Copyright (c) 2016 The angular-translate team, Pascal Precht; Licensed MIT
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module unless amdModuleId is set
define([], function () {
return (factory());
});
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
factory();
}
}(this, function () {
angular.module('pascalprecht.translate')
/**
* @ngdoc object
* @name pascalprecht.translate.$translateStylesheetFiles
* @requires $compile
* @requires $http
*
* @description
* Complete manual: https://gist.github.com/w3core/1417a245ca42559d8cb9c45dc2740da6
* Translates all textual parts of "content" rule in CSS stylesheet file.
* Accepts an optional `translate-values` interpolate parameters to pass
* dynamized values though translation.
*
* @param {string=} translate-values Values to pass into translation id. Can be passed as object literal string or interpolated object.
*
* @example
<example>
<file name="index.html">
<link translate rel="stylesheet" href="style.css" />
<link translate translate-values="{{values}}" rel="stylesheet" href="style.css" />
<link translate translate-values="{value: 5}" rel="stylesheet" href="style.css" />
</file>
<file name="style.css">
.selector-1:before {
content: "TRANSLATION_ID";
display: block;
color: red;
}
.selector-2:after {
content: "Not translatable value because 'translate:none' property defined";
translate: none;
}
.selector-3:before {
content: attr(attribute-A) "Some translation ID" attr(attribute-B);
}
</file>
</example>
*
* @author Max Chuhryaev
*/
.directive('link', $translateStylesheetFiles);
function $translateStylesheetFiles ($compile, $http) {
function clearHREF (href, base) {
var s = (typeof href == 'string') ? href.replace(/([?#].*)$/i, '') : '';
if (base) s = s.replace(/[^\/]+$/i, '');
return s;
}
function extractContentProps (string) {
var array = [];
string
.replace(/\/\*[\s\S]*?\*\//g, ' ')
.replace(/@[-a-zA-Z]*?keyframes[^{]+\{[\s\S]+?}\s*}/g, ' ')
.replace(/(@media[^{]+)\{([\s\S]+?})\s*}/g, function ($0, $1, $2){
var list = extractContentProps($2);
for (var i=0; i<list.length; i++) {
list[i].media = $1;
array.push(list[i]);
}
return ' ';
})
.replace(/[^}]+{[^}]*?translate\s*\:\s*none\s*[;\r\n\t]+?[^}]*?}/g, ' ')
.replace(/([^}]+){[^}]*?content[: ]+([^;}\r\n\t]+)[;\r\n\t]*?[^}]*?}/g, function($0, $1, $2){
if (!hasTranslatableContent($2)) return;
array.push({ selector:$1, content:$2 });
});
return array;
}
function hasTranslatableContent (string) {
var result = false;
string.replace(/(["'])((?:(?!\1)[^\\]|(?:\\\\)*\\[^\\])*)\1/g, function(string, quote, value){
if (value.trim().length) result = true;
});
return result;
}
function prepareContentProperty (string, vars) {
var $var, $obj;
if (typeof vars == "string" && vars.trim()) {
var values = /^\s*\{\{\s*([^{}]+?)\s*\}\}\s*$/.exec(vars);
if (values && values[1].trim()) $var = values[1].trim();
else if (/^\s*\{[^{].+[^}]\}\s*$/.exec(vars)) $obj = vars.trim();
}
return string.replace(/(["'])((?:(?!\1)[^\\]|(?:\\\\)*\\[^\\])*)\1/g, function(string, quote){
var antiquote = quote == '"' ? "'" : '"';
var vars = $var ? (":" + $var)
: $obj ? (":" + antiquote + $obj + antiquote)
: '';
return quote + "{{" + string + "|translate"+vars+"}}" + quote;
});
}
function insertAfter (newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
return {
restrict: 'E',
compile: function ($element, $attr) {
if (typeof $attr.notTranslate != 'undefined') return;
if (typeof $attr.translate != 'undefined' && clearHREF($attr.href).match(/\.css$/i)) {
return function ($scope, $element, $attr) {
var node = document.createElement("style");
node.setAttribute("translation-href", $attr.href);
insertAfter(node, $element[0]);
$http.get($attr.href).success(function(string){
var css = "", array = extractContentProps(string);
for (var i=0; i<array.length; i++) {
if (array[i].media && array[i].media.trim()) css+= array[i].media + "{\n";
css += array[i].selector.trim()
+ "{content:"
+ prepareContentProperty(array[i].content, $attr.translateValues).replace(/(\\)/g, "\\\\")
+ ";}\n"
;
if (array[i].media && array[i].media.trim()) css+= "}\n";
}
if (!css.length) return (!node.parentNode.removeChild(node));
if (node.styleSheet) node.styleSheet.cssText = css;
else node.appendChild(document.createTextNode(css));
$compile(node)($scope);
});
};
}
}
};
}
$translateStylesheetFiles.$inject = ['$compile', '$http'];
$translateStylesheetFiles.displayName = '$translateStylesheetFiles';
return 'pascalprecht.translate';
}));
@w3core
Copy link
Author

w3core commented May 18, 2016

Angular Translate Stylesheet Files

Translates all textual parts of content rule in CSS stylesheet file.

  • Accepts as an optional translate-values interpolate parameters to pass dynamic values through translation.
  • Provides an optional CSS property translate with value none to prevent translation of current CSS rule-set.
  • Accepts as an optional not-translate parameter to prevent translation of stylesheet file.

Quick Start

Dependencies

Make sure to embed it in your HTML document.

<script src="path/to/angular.js"></script>
<script src="path/to/angular-translate.js"></script>
<script src="path/to/angular-translate-stylesheet-files.js"></script>

Make sure that generic logic for angular-translate was integrated and then you can start working with CSS.

Add attribute translate to enable translation of stylesheet file.

<link translate rel="stylesheet" href="path/to/stylesheet.css" />

You can use an optional attribute translate-values to pass dynamic values through translation.

<link translate translate-values="{page:1, amount:5}" rel="stylesheet" href="..." />
<link translate translate-values="{{valuesObject}}" rel="stylesheet" href="..." />

Also you can use an optional CSS property translate with value none to prevent translation of any CSS rule-set inside CSS file.

.selector:before {
    content: "Not translatable string";
    translate: none;
}

An example of some stylesheet file:

.selector-a:before {
    /* Will not translated because "translate:none;" rule defined */
    content: "Not translatable string";
    translate: none;
}
.selector-b:after {
    content: "Translatable string";
    display: block;
    color: green;
}
.selector-c:before {
    content: attr(title) "Translatable string" attr(href);
    display: block;
    color: blue;
}

Voila

anim

Known conflicts

This library working correctly witn <link> tags and does not replaces it. It creates another one <style> tag that extends an original stylesheets. But make sure that you have not using any CSS preprocessing javascript libraries which can to remove <link> node that leads to prevent of translation logic. For example -prefix-free javascript library replaces <link> node to compiled <style> node. In this case you should implement delayed loading logic for all libraries like this. Following code demonstrates how to implement delayed loading logic in your AngularJS application:

angular
.module('app', ['pascalprecht.translate'])
.run(function ($rootScope) {
  var $off = $rootScope.$on('$viewContentLoaded', function () {
    $.getScript("/path/to/prefixfree.js");
    $off();
  });
});

-- Have a nice day

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment