簡體   English   中英

Symfony2和Angular.JS之間共享的翻譯

[英]Translations shared between Symfony2 and Angular.JS

我正在尋找一種在我的Symfony2應用程序和Angular.JS應用程序之間共享我的翻譯的好方法,它們本質上是同一應用程序,但是它們的行為卻大不相同。

首先,我在我的angular js應用程序中做了一個允許使用[[variable]]進行模板化的指令,因此我可以將twig中的變量與我的angular變量一起使用。

所以我想要實現的是將我的句子翻譯成用戶喜歡的本地化。

所以我的結論是,我想使用Symfony的翻譯功能,因為在Angular中對此沒有很好的支持。 我還想使用Symfony包括的所有內置功能,例如自動更新文件,支持YAML等。但是我需要弄清楚如何將它們傳輸到我的JS應用程序以及如何在Symfony中檢測到它們,因此兩個應用程序都可以使用它們。

所以我最初的想法是:

更改翻譯的捕獲方式,或者至少在范圍內添加一個翻譯。

{% trans %}Hello [[name]]{% endtrans %}

<trans name="My Name" translation="Hello [[name]]" />

並且還支持帶有復數形式的所有其他功能。

然后,這將生成一個包含翻譯和復數形式的文件。

通過有角度的ajax調用請求語言,並將其保存到客戶端的本地存儲中。

這將允許我用適當的值替換上面的trans指令。 這不是設置問題。 但是,它需要從angular可以讀取的任何格式導出為JSON。

然后需要一個匹配器,並且需要對復數和所有其他可用功能的支持。

其他想法

通常,在使用Angular.JS App時最好不使用Symfony2翻譯,從而僅使用角度翻譯,否則用Symfony2 Twigs編寫並翻譯的文本可能無法在Angular中翻譯。 但是,如果Symfony可以捕獲並吐出這些文件,我會發現更好。

我認為這需要扎實的工作,但是我認為這需要解決。 感謝任何想法和有用的評論,我正在考慮在GitHub上為此啟動一個項目,以對此提供適當的支持。 但是,如果已經有了這樣的方法,那就更好了。

/馬庫斯

這是使用自定義角度濾鏡簡化標記的建議

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

進一步的研究

這是我的應用程序:

'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/

這可以解決:

  • 異步獲取翻譯
  • 緩存翻譯,因此切換速度非常快
  • 使用具有值的輸入/字段
  • 將語言存儲在本地存儲中
  • 在翻譯更改時動態更新整個DOM
  • 真的很快,至少我嘗試過的規模
  • 針對一個常見問題的完整且有效的解決方案

要解決的事情:

  • 移出代碼,檢查它是否是其他地方的輸入字段
  • 名字的復數
  • 輸入占位符
  • 翻譯支持的其他功能。
  • 支持參數,請參見示例:

<trans translation="Hello {{name}}" name="{{name}}">

  • 使用xgettext掃描項目? 並生成可以在某些軟件中翻譯的YML或類似結構。
  • 刪除臨時解決方案,並使用已注釋掉的有效解決方案。

其他的建議

請隨時提出問題,如果有,我會提供信息,並且可能會在不久之后提供jsFiddle。

/馬庫斯

我最終使用了該解決方案。 解決我所有的問題:

http://cliffmeyers.com/blog/2013/3/11/integration-angularjs

因此,我走得更遠,經過一些研究,為該操作使用過濾器更為有意義,但是我似乎無法按預期的方式使它工作。

所以這就是我申請的東西:

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 /承諾錯誤嗎? 請給我一些幫助。

但是,如果我通過將其分配給控制器中的值來使用它,則可以正常工作。

在控制器中,我這樣做:

app.controller('PageController',
    function PageController($scope, wordService) {
         $scope.someValue = wordService.getWord("USER");
    }
);

然后在html中使用它:

{{someValue}}

而且效果很好。

/馬庫斯

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM