Маршрутизация angularjs i18n с параметрами пути

В одном приложении, с которым я работаю, система маршрутизации должна быть интегрирована с i18n, как в примере ниже:

$routeProvider.when('/:i18n/section', ...);

Но я столкнулся с некоторыми проблемами из-за, как мне кажется, цикла $digest, который не меняет параметр i18n во время выполнения.

Другая проблема, с которой я сталкиваюсь, заключается в том, что путь к местоположению указывает на что-то вроде:

http://localhost:9000/section/...

не как:

http://localhost:9000/en/section/...

параметр пути i18n завершается ассоциацией с /section/, что означает, что в сервисе $routeParams $routeParams.i18n = 'section';. Это ожидается, но мне нужно иметь возможность проанализировать параметр /:i18n/, чтобы избежать конфликтов, и изменить URL-адрес, объединив одну локаль, чтобы контекстуализировать сеанс, заменив текущий маршрут новым, i18n-ized, без обновления представление/приложение автоматически, но выборочно, потому что нужно изменить только некоторые функции, а не все.

Кроме того, я разработал один сервис, который на основе списка возможных языковых настроек и их веса оценивает язык, который будет выбран в текущем контексте:

var criteria = {
    default: {
        tag: 'en',
        weight: 10
    },
    user: {
        tag: 'pt',
        weight: 20
    },
    cookie: {
        tag: 'zh',
        weight: 30
    },
    url: {
        tag: 'ru',
        weight: 40
    },
    runtime: {
        tag: 'es',
        weight: 50
    }
};

function chooseLanguage (languages) {
    var deferred = $q.defer();
    var weights = [];
    var competitors = {};
    var choosen = null;

    if (defineType(languages) === 'array') {
        for (var i = languages.length - 1; i >= 0; i--) {
            if (languages[i].tag !== null) {
                weights.push(languages[i].weight);

                competitors[languages[i].weight] = languages[i];
            }
        }

        choosen = competitors[Math.max.apply(Math, weights)];
    } else if (defineType(languages) === 'object') {
        choosen = languages;
    } else {
        return;
    }

    setRuntimeLanguage(choosen.tag);

    deferred.resolve(choosen);

    return deferred.promise;
}

Объясняя приведенный выше код, когда angular загружает приложение, выполняется фрагмент кода, выбирая, какой язык определен и достаточно ли он силен для выбора. С этой операцией связаны и другие методы, такие как определение параметра URL, если есть один зарегистрированный пользователь и т. д., и процесс выполняется не только при начальной загрузке, но и в нескольких контекстах: событие $routeChangeStart, когда пользователь аутентифицирует свою сессию, переключение языков в поле выбора и так далее.

Итак, в резюме мне нужно уметь:

  1. Проанализируйте URL-адрес и правильно примените параметр локали, если он не был сообщен изначально;
  2. Измените параметр URL i18n во время выполнения, не перезагружая все представление/приложение;
  3. Работайте с языковыми изменениями правильно, что означает, что если мой подход, основанный на весах, не лучший способ, если вы предложите мне что-то еще.

Редактировать 1:

$watcher не работает, потому что приложению нужна правильная локаль в каждом пути, даже до того, как оно создаст экземпляры всех элементов. AngularJS используется на каждом этапе этой проверки, но если есть какая-то подсказка, чтобы сделать это снаружи, до создания экземпляра Angular, мы можем обсудить это.

На данный момент я использую принятый ответ ниже с решением, которое я разработал, но его необходимо улучшить.


person Mateus Leon    schedule 04.03.2015    source источник
comment
Не могли бы вы назначить $watcher для проверки изменений в переменной i18n, что означает, что когда i18n изменяется во время выполнения, это изменение распространяется по всему приложению. У вас также есть в вашем распоряжении не только блок конфигурации, который запускается первым и где вы назначаете маршрутизатор, но и блок запуска, который запускается позже.   -  person Samir Alajmovic    schedule 04.03.2015
comment
Я попробую определить наблюдателя, но если я изменю $location.path(), приложение не перезагрузится?   -  person Mateus Leon    schedule 04.03.2015
comment
Он не перезагружает все приложение, если я не ошибаюсь, но перезагружает любой контроллер, определенный на этом маршруте. Блок config/run запускается только в начале, когда он загружает все это.   -  person Samir Alajmovic    schedule 04.03.2015


Ответы (1)


Я закончил тем, что сделал своего рода предварительный анализ URL-адреса. Используя только ngRoute (ui-router не вариант...), я проверяю, соответствует ли путь ограничениям, если нет, то срабатывает перенаправление, правильно определяющее путь.

Ниже следует фрагмент решения для основного маршрута и простой последующий пример из-за количества маршрутов в приложении и их конкретных данных, которые не относятся к базовой идее:

$routeProvider
    .otherwise({
        redirectTo: function () {
            return '/404/';
        }
    })

    .when('/:i18n/', {
        redirectPath: '/:i18n/',
        templateUrl: 'home.html',
        controller: 'HomeCtrl',
        resolve: {
            i18n: [
                '$location',
                '$route',
                'i18nService',
                function ($location, $route, i18nService) {
                    var params = $route.current.params;

                    return i18nService.init(params.i18n)
                        .then(null, function (fallback) {
                            if (params.i18n === '404') {
                                return $location.path('/' + params.i18n + '/404/').search({}).replace();
                            }

                            $location.path('/' + fallback).search({}).replace();
                        });
                }
            ],
            data: [
                'dataService',
                function (dataService) {
                    return dataService.resolve();
                }
            ]
        }
    })

    .when('/:i18n/search/:search/', {
        redirectPath: '/:i18n/search/:search/',
        templateUrl: 'search.html',
        controller: 'SearchCtrl',
        resolve: {
            i18n: [
                '$location',
                '$route',
                'i18nService',
                function ($location, $route, i18nService) {
                    var params = $route.current.params;

                    return i18nService.init(params.i18n)
                        .then(null, function (fallback) {
                            $location.path('/' + fallback + '/search/').search({
                                search: params.search
                            }).replace();
                        });
                }
            ],
            data: [
                '$route',
                'searchService',
                function ($route, searchService) {
                    var params = $route.current.params;

                    return searchService.resolve({
                        'search': params.search
                    });
                }
            ]
        }
    });

Свойство redirectPath используется в случае, если вам нужен шаблон для этого маршрута, потому что ngRoute хранит одну копию для частного использования, но не предоставляет к ней доступ с $route публичными свойствами.

Из-за обязательного синтаксического анализа перед любым запросом приложения (кроме i18nService, который загружает список локалей и запускает angular-translate для загрузки переводов), этот метод вызывает несколько перенаправлений, что приводит к значительной задержке создания экземпляра.

Если возможны какие-либо улучшения, я также буду благодарен, если я найду лучший способ сделать это, я обновлю этот ответ.

person Mateus Leon    schedule 18.05.2015