我正在寻找一种在我的Symfony2应用程序和我的Angular.JS应用程序之间共享翻译的好方法,它们本质上是相同的应用程序,但它们的行为却截然不同。
首先,我已经制定了一个指令,允许在我的角度js app中使用[[variable]]进行模板化,所以我可以使用twig中的变量和我的变量角度。
所以我想要实现的是将我的句子翻译成用户喜欢的本地化。
所以我的结论是我想使用Symfony的翻译功能,因为在Angular中没有很好的支持。我还想使用Symfony包含的所有内置功能,这就像自动更新文件,支持YAML等。但我需要弄清楚如何将它们传输到我的JS应用程序以及如何在Symfony中检测它们,所以两个应用程序都可以使用它们。
所以我最初的想法是:
更改翻译的捕获或至少在范围中添加一个。
{% trans %}Hello [[name]]{% endtrans %}
<trans name="My Name" translation="Hello [[name]]" />
并且还支持所有其他具有复数等功能。
然后,这将生成具有翻译和复数等的文件。
通过有角度的ajax调用请求语言并将其保存到客户端的本地存储中。
这将允许我用正确的值替换trans的上面的指令。 这不是设置的问题。但是,它需要从任何格式导出到角度可以读取的JSON。
然后需要一个匹配器,并且需要支持多元化和所有其他可用功能。
其他想法
一般来说,在使用Angular.JS应用程序时不使用Symfony2翻译可能会更好,因此只使用角度翻译,否则用Symfony2 Twigs编写并翻译的文本可能无法在Angular中翻译。但是,如果Symfony可以捕获并吐出这些文件,我发现它会更好。
我认为这需要做一些坚实的工作,但我觉得这需要解决。任何想法和有用的评论都表示赞赏,我正在考虑在GitHub上为此启动一个项目,以便为此提供适当的支持。但是,如果已经有这样的话,可能只会更好地使用它。
/马库斯
答案 0 :(得分:1)
这是一个使用自定义角度过滤器来简化标记的建议
HTML:
<div ng-app="myApp" ng-controller="MainCtrl">
{{ item |translate }}
</div>
JS
var words={
'fr': {'Bus': "AutoBus"}
};
var app = angular.module('myApp', []);
app.constant('lang','fr');
app.factory('wordService',function(lang){
return {
getWord:function(val){
return words[lang][val];
}
}
})
app.filter('translate', function(wordService){
return function(val){
return wordService.getWord(val)
}
})
app.controller('MainCtrl', function($scope) {
$scope.item = 'Bus';
});
您可以使用服务从服务器或localStorage请求转换文件(如果它们已存在)。只需在运行时设置语言。
您可以以任何适合您的方式重新配置单词对象,以便在两个应用程序中使用它。
的 DEMO 强>
答案 1 :(得分:1)
进一步研究
这是我的应用程序:
'use strict';
var myApp = angular.module('myApp', []);
这是我的控制器:
'use strict';
myApp.controller('PageController',
function PageController($scope, translationService, $rootScope) {
$rootScope.currentLanguage = 'en';
$rootScope.translations = translationService.getTranslations($scope.currentLanguage);
$scope.setLanguage = function(language) {
if (language === $scope.currentLanguage) {
return;
}
$rootScope.currentLanguage = language;
$rootScope.translations = translationService.getTranslations($scope.currentLanguage);
}
}
);
这是translationService:
'use strict';
myApp.service('translationService', function ($http, $q) {
var translationsCache = {};
return {
getTranslations: function(language) {
if (translationsCache[language]) {
return translationsCache[language];
}
var deferred = $q.defer();
// **** FAKE SOLUTION **** //
// I just return a resolve here as it doesn't really matter for this test.
if (language == 'sv') {
deferred.resolve({
"My first text unit": "Detta är min första text unit",
"I am a Pirate": "Jag är en Pirat"
});
} else if (language == 'en') {
deferred.resolve({
"My first text unit": "This is my first Text unit",
"I am a Pirate": "I'm a Pirate"
});
}
translationsCache[language] = deferred.promise;
return deferred.promise;
// **** END FAKE SOLUTION **** //
/*
// **** WORKING SOLUTION **** //
The probable real solution to fetching language JSON generated by Symfony somewhere
$http({method: 'GET', url: '/translations/'+language}).
success(function (data, status, headers, config) {
deferred.resolve(data);
}).
error(function(data, status, headers, config) {
deferred.reject(status);
});
translationsCache[language] = deferred.promise;
return deferred.promise;
// **** END WORKING SOLUTION **** //
*/
}
}
});
所以这是我在经过一些试验和错误后提出的指示:
myApp.directive('translation', function($rootScope) {
return {
restrict: 'A', // Restrict to attribute
replace: true, // Replace current object by default, not for input though, see solution below
link: function(scope, element, attrs, controller){
// This will watch for changes in currentLanguage in your $rootScope
scope.$watch(function() {
return $rootScope.currentLanguage; // If this changes then trigger function (binding)
}, function(newVal, oldVal) {
// As we have translation as a promise this is how we do
$rootScope.translations.then(function(translations) {
// Does it exist, then translate it, otherwise use default as fallback
if (translations[scope.translation]) {
// Just some extra I found could be useful, set value if it is a button or submit. Could be extended.
if (element.prop('tagName') === 'INPUT' && (element.prop('type') === 'button' || element.prop('type') === 'submit')) {
return angular.element(element).val(translations[scope.translation]);
}
// Else just change the object to be the new translation.
return element.html(translations[scope.translation]);
}
// This is the fallback, and same as above, button and submit
if (element.prop('tagName') === 'INPUT' && (element.prop('type') === 'button' || element.prop('type') === 'submit')) {
return element.val(scope.translation);
}
return element.html(scope.translation);
});
});
},
scope: {
translation: "@" // Save the parameter to the scope as a string
}
}
});
这里有一些如何使用它的例子。
HTML:
<div class="container">
<div class="nav">
<button ng-click="setLanguage('en')">
<trans translation="English" />
</button>
<button ng-click="setLanguage('sv')">
<trans translation="Svenska" />
</button>
</div>
<hr />
<p><trans translation="I am a Pirate" /></p>
<p><trans translation="A text unit that doesn't exist" /></p>
<p><input type="button" translation="My button" /></p>
</div>
这将使用jsFiddle如下: http://jsfiddle.net/Oldek/95AH3/4/
这解决了这个问题:
要解决的问题:
<trans translation="Hello {{name}}" name="{{name}}">
其他评论
如果您有问题,请随时提出问题,我会尽快提供信息,并且很快就可以提供jsFiddle。
/马库斯
答案 2 :(得分:1)
我结束使用此解决方案。解决了我所有的问题:
答案 3 :(得分:0)
所以我进一步了解,经过一些研究后,使用过滤器进行此操作更有意义,但是我似乎无法按照我的意愿使用它。
所以这就是我申请的目的:
var app = angular.module('app', []);
app.factory('translationsService', function($http, translations, $q) {
return {
getTranslations: function(lang) {
var deferred = $q.defer();
$http({method: 'GET', url: '/translations/'+lang}).
success(function (data) {
deferred.resolve({
data: data,
getWord: function(word) {
return data[word] ? data[word] : word;
}
});
});
return deferred.promise;
}
}
});
app.factory('wordService', function(translationsService, $q){
return {
lang: 'en-us',
getWord: function(val){
var translations = translationsService.getTranslations(this.lang);
var deferred = $q.defer();
translations.then(function(data) {
deferred.resolve(data.getWord(val));
});
return deferred.promise;
}
}
});
app.filter('translate', function(wordService){
return function(val){
return wordService.getWord(val);
}
});
所以如果我现在在html页面中这样做:
{{ "User" | translate }}
然后我以无限循环结束。我得到了整个$ q / promise的错误吗?我在这里需要一些帮助。
但是,如果我通过将它分配给控制器中的值来使用它,它可以正常工作。
在控制器中我做:
app.controller('PageController',
function PageController($scope, wordService) {
$scope.someValue = wordService.getWord("USER");
}
);
然后在html中使用它:
{{someValue}}
它运作得很好。
/马库斯