2

Symfony2 アプリと Angular.JS アプリの間で翻訳を共有する良い方法を探しています。これらは基本的に同じアプリですが、動作が大きく異なります。

まず、Angular js アプリで [[ variable ]] を使用してテンプレートを作成できるようにするディレクティブを作成したので、Angular の変数と一緒に twig の変数を使用できます。

だから私が達成したいのは、私の文章をユーザーが好むローカリゼーションに翻訳することです。

私の結論は、Angular には適切なサポートがないため、Symfony の翻訳機能を使用したいということです。また、ファイルの自動更新や YAML のサポートなど、Symfony に組み込まれているすべての組み込み機能を使用したいと考えています。ただし、それらを JS アプリケーションに転送する方法と、Symfony でそれらを検出する方法を検討する必要があります。両方のアプリケーションで使用できます。

だから私の最初のアイデア:

翻訳のキャプチャを変更するか、少なくともスコープに別の翻訳を追加してください。

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

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

また、複数形などの他のすべての機能もサポートしています。

これにより、翻訳や複数形などを含むファイルが生成されます。

angular ajax 呼び出しを介して言語を要求し、これをクライアント側のローカル ストレージに保存します。

これにより、トランスの上記のディレクティブを適切な値に置き換えることができます。これは、セットアップの問題ではありません。ただし、どのような形式からでも、angular が読み取れる JSON にエクスポートする必要があります。

次に、マッチャーが必要であり、複数形のサポートと利用可能な他のすべての機能が必要です。

その他のアイデア

一般に、Angular.JS アプリを実行するときは Symfony2 の翻訳を使用せず、角度付きの翻訳のみを使用する方がよい場合があります。そうしないと、Symfony2 Twigs で記述され、翻訳されたテキストは、おそらく Angular で翻訳できなくなります。しかし、これらのファイルの生成は、Symfony がキャプチャして吐き出すことができれば、より良いと思います。

これはしっかりするためにはちょっとした作業が必要だと思いますが、これを解決する必要があると感じています。アイデアや有益なコメントをお待ちしております。適切なサポートを提供するために、GitHub でこのプロジェクトを開始することを検討しています。しかし、すでにそのようなものがある場合は、それを使用する方が良いかもしれません.

/マーカス

4

4 に答える 4

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

于 2013-10-30T17:00:28.757 に答える
1

私はこのソリューションを使用することになりました。私の問題をすべて解決します:

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

于 2013-11-09T18:33:02.517 に答える
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/

これで解決します:

  • 翻訳を非同期で取得する
  • 翻訳をキャッシュするので、切り替えは非常に高速です
  • 値を持つ入力/フィールドの操作
  • 言語をローカル ストレージに保存する
  • 翻訳の変更時に DOM 全体を動的に更新する
  • 本当に速い、少なくとも私が試したスケール
  • 一般的な問題に対する完全かつ実用的なソリューション

解決すべきこと:

  • それが入力フィールドであるかどうかをチェックするコードを別の場所に移動します
  • 複数化
  • 入力プレースホルダー
  • 翻訳がサポートするその他の機能。
  • サポート パラメータについては、例を参照してください。

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

  • プロジェクトを xgettext でスキャンしますか? 一部のソフトウェアで変換できる YML または同様の構造を生成します。
  • 一時的な解決策を削除し、コメントアウトされた作業中の解決策を使用してください。

他のコメント

質問がある場合はお気軽にお尋ねください。情報を提供します。おそらく jsFiddle がすぐに必要になると思われます。

/マーカス

于 2013-10-31T12:36:36.603 に答える
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で使用します:

{{いくつかの値}}

そして、それはうまく機能します。

/マーカス

于 2013-11-07T16:20:07.830 に答える