さらなる研究
これが私のアプリです:
'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 がすぐに必要になると思われます。
/マーカス