diff --git a/modules/i18n/test/i18nSpec.js b/modules/i18n/test/i18nSpec.js
new file mode 100644
index 00000000..dfd55679
--- /dev/null
+++ b/modules/i18n/test/i18nSpec.js
@@ -0,0 +1,39 @@
+/*describe('i18n', function() {
+ 'use strict';
+ var uiI18n, element;
+ beforeEach(function(){
+ uiI18n = angular.module('ui.i18n');
+ uiI18n.add(['en', 'en-us'],{
+ groupPanel:{
+ testingMerge: 'some text',
+ otherText: 'some other group property text',
+ description:'Drag a column header here and drop it to group by that column.'
+ },
+ example: 'I speak English',
+ anotherExample: 'I speak A Different Language'
+ });
+ uiI18n.add('de',{
+ groupPanel:{
+ otherText: 'eine andere gruppe eigenschaft text',
+ description:'Ziehen Sie eine Spaltenuberschrift hierhin um nach dieser Spalte zu gruppieren.'
+ },
+ example: 'Ich spreche Deutsch'
+ });
+ uiI18n.add('de',{
+ groupPanel:{
+ testingMerge: 'falaffels are spelled horribly...',
+ },
+ anotherExample: 'I speak A Different Language'
+ });
+ uiI18n.set('en-us');
+ });
+
+ it('should translate', function () {
+ inject(function ($rootScope, $compile) {
+ element = $compile('
')($rootScope);
+ console.log(element);
+ expect(element.html()).toBe('some other group property text');
+ });
+ });
+});
+*/
\ No newline at end of file
diff --git a/modules/i18n/ui-i18n.js b/modules/i18n/ui-i18n.js
new file mode 100644
index 00000000..294d950d
--- /dev/null
+++ b/modules/i18n/ui-i18n.js
@@ -0,0 +1,290 @@
+/**
+ * ui-i18n Created by Tim Sweet on 2/1/14.
+ * https://github.com/timothyswt
+ * MIT License
+ */
+ /**
+ * @ngdoc directive
+ * @name ui-i18n
+ * @requires $parse
+ *
+ * @description
+ * Allows you to localize your project by being able to specify a language on any root-ish element
+ * this can be a bindable property or the string.
+ * if bound it will automatically update all children when update.
+ * @example
+
+
+
+
+
+
+
+ */
+
+(function(deepExtend){
+ 'use strict';
+ var MISSING = '[MISSING]: ',
+ UPDATE_EVENT = '$uiI18n',
+ FILTER_ALIASES = ['t', 'translate'],
+ DIRECTIVE_ALIASES = ['uiT', 'uiTranslate'],
+ LOCALE_DIRECTIVE_ALIAS = 'uiI18n',
+ // default to english
+ DEFAULT_LANG = 'en-US',
+ langCache = {
+ _langs: {},
+ current: null
+ },
+ uiI18n = angular.module('ui.i18n', []);
+
+ langCache.get = function(lang){
+ return langCache._langs[lang.toLowerCase()];
+ };
+ langCache.add = function(lang, strings){
+ var lower = lang.toLowerCase();
+ var cache = langCache._langs;
+ cache[lower] = deepExtend(cache[lower] || {}, strings);
+ };
+ langCache.setCurrent = function(lang){
+ langCache.current = lang.toLowerCase();
+ };
+ langCache.getCurrent = function(){
+ return langCache.get(langCache.current);
+ };
+
+ uiI18n._cache = langCache;
+ uiI18n.$broadcast = function(lang){
+ if (lang && this.$root){
+ uiI18n.$root.$broadcast(UPDATE_EVENT, lang);
+ }
+ };
+ uiI18n.add = function(langs, strings){
+ if (typeof(langs) === 'object'){
+ angular.forEach(langs, function(lang){
+ if (lang){
+ langCache.add(lang, strings);
+ }
+ });
+ } else {
+ langCache.add(langs, strings);
+ }
+ };
+ uiI18n.set = function(lang){
+ if (lang){
+ langCache.setCurrent(lang);
+ uiI18n.$broadcast(lang);
+ }
+ };
+
+ var localeDirective = function() {
+ return {
+ compile: function(){
+ return {
+ pre: function($scope, $elm, $attrs) {
+ var alias = LOCALE_DIRECTIVE_ALIAS;
+ if (!uiI18n.$root){
+ uiI18n.$root = $scope.$root;
+ }
+ // check for watchable property
+ var lang = $scope.$eval($attrs[alias]);
+ if (lang){
+ $scope.$watch($attrs[alias], uiI18n.set);
+ } else if ($attrs.$$observers){
+ $scope.$on('$destroy', $attrs.$observe(alias, uiI18n.set));
+ } else {
+ // fall back to the string value
+ lang = $attrs[alias];
+ }
+ uiI18n.set(lang || DEFAULT_LANG);
+ }
+ };
+ }
+ };
+ };
+ uiI18n.directive(LOCALE_DIRECTIVE_ALIAS, localeDirective);
+/**
+ * * @ngdoc directive
+ * @name ui-t,ui-Translate
+ * @requires $parse
+ *
+ * @description
+ * specify the i18n string to use
+ * this can be a bindable property, expression/partial expression, or the object token.
+ * @example
+
+
+
+
+
+
+
+
+
+
+
groupPanel.testingMerge
+
+
example
+
+
+
+
invalid.path
+
+
invalid.translation.again
+
+
+
Using element:
+
example
+
+
groupPanel.description
+
+
invalid.path
+
+
invalid.translation.again
+
+
Using Translate Filters:
+
+
{{"groupPanel.description" | t}}
+
+
{{"example" | t}}
+
{{"invalid.path" | t}}
+
+
{{"invalid.translation.again" | translate}}
+
+
+
+ var app = angular.module('app', ['ui.i18n']);
+
+ app.controller('main', ['$scope', function($scope){
+ $scope.language = "en";
+ $scope.hello = "ui-i18n Example";
+ $scope.desc = "description";
+ $scope.changeLanguage = function(){
+ $scope.language = $scope.language == "de" ? "en" : "de";
+ };
+ $scope.changeDesc = function(){
+ $scope.desc = $scope.desc == "description" ? "otherText" : "description";
+ };
+ }]);
+ //Declare your i18n strings, this is enclosed in order to show that this can be done anywhere in the application
+ (function(){
+ var uiI18n = angular.module('ui.i18n');
+ uiI18n.add(["en", "en-us"],{
+ groupPanel:{
+ testingMerge: 'some text',
+ otherText: 'some other group property text',
+ description:'Drag a column header here and drop it to group by that column.'
+ },
+ example: "I speak English",
+ anotherExample: "I speak A Different Language"
+ });
+ })();
+
+ (function(){
+ var uiI18n = angular.module('ui.i18n');
+ uiI18n.add("de",{
+ groupPanel:{
+ otherText: 'eine andere gruppe eigenschaft text',
+ description:'Ziehen Sie eine Spaltenüberschrift hierhin um nach dieser Spalte zu gruppieren.'
+ },
+ example: "Ich spreche Deutsch"
+ });
+ })();
+
+ (function(){
+ var uiI18n = angular.module('ui.i18n');
+ uiI18n.add("de",{
+ groupPanel:{
+ testingMerge: 'falaffels are spelled horribly...',
+ },
+ anotherExample: "I speak A Different Language"
+ });
+ })();
+
+
+ **/
+ // directive syntax
+ var uitDirective = function($parse) {
+ return {
+ restrict: 'EA',
+ compile: function(){
+ return {
+ pre: function($scope, $elm, $attrs) {
+ if (!uiI18n.$root){
+ uiI18n.$root = $scope.$root;
+ }
+ var alias1 = DIRECTIVE_ALIASES[0],
+ alias2 = DIRECTIVE_ALIASES[1];
+ var token = $attrs[alias1] || $attrs[alias2] || $elm.html();
+ var missing = MISSING + token;
+ var observer;
+ if ($attrs.$$observers){
+ var prop = $attrs[alias1] ? alias1 : alias2;
+ observer = $attrs.$observe(prop, function(result){
+ if (result){
+ $elm.html($parse(result)(langCache.getCurrent()) || missing);
+ }
+ });
+ }
+ var getter = $parse(token);
+ var listener = $scope.$on(UPDATE_EVENT, function(evt, lang){
+ if (observer){
+ observer($attrs[alias1] || $attrs[alias2]);
+ } else {
+ // set text based on i18n current language
+ $elm.html(getter(langCache.get(lang)) || missing);
+ }
+ });
+ $scope.$on('$destroy', listener);
+ }
+ };
+ }
+ };
+ };
+
+ // optional filter syntax
+ var uitFilter = function($parse) {
+ return function(data) {
+ var getter = $parse(data);
+ // set text based on i18n current language
+ return getter(langCache.getCurrent()) || MISSING + data;
+ };
+ };
+
+ angular.forEach(DIRECTIVE_ALIASES, function(alias){
+ uiI18n.directive(alias,['$parse', uitDirective]);
+ });
+ angular.forEach(FILTER_ALIASES, function(alias){
+ uiI18n.filter(alias,['$parse', uitFilter]);
+ });
+ uiI18n.service('$i18nService', function(){
+ var uii18nService = {
+ getCache: function(){
+ return uiI18n._cache;
+ },
+ $broadcast: function(){
+ uiI18n.$broadcast(arguments);
+ },
+ add: function(langs, strings){
+ uiI18n.add(langs, strings);
+ },
+ set: function(lang){
+ uiI18n.set(lang);
+ }
+ };
+ return uii18nService;
+ });
+})(function deepExtend(destination, source) {
+ 'use strict';
+ // adding deep copy method until angularjs supports deep copy like everyone else.
+ // https://github.com/angular/angular.js/pull/5059
+ for (var property in source) {
+ if (source[property] && source[property].constructor &&
+ source[property].constructor === Object) {
+ destination[property] = destination[property] || {};
+ deepExtend(destination[property], source[property]);
+ } else {
+ destination[property] = source[property];
+ }
+ }
+ return destination;
+});
\ No newline at end of file