1
votes

I have a library that allows a user to switch between English and Welsh language during a browser session which works with Play 2.3.

After switching to Play 2.5 this library no longer works. I have tried following the 'documentation' at https://www.playframework.com/documentation/2.5.x/ScalaI18N but so far I have had no luck.

My implementation so far is:

Twirl template containing the switch

@import play.api.Play.current
@import play.api.i18n.Messages.Implicits._
@import uk.my-org.urls.Link

@(langMap  : Map[String, Lang], langToCall : (String => Call),     customClass: Option[String] = None)(implicit lang: Lang)

<p>Current = @lang</p>

<p class="@if(customClass.isDefined) {@customClass.get}">
@langMap.map { case (key: String, value: Lang) =>
        @if(lang.code != value.code) {
            @Link.toInternalPage(
                id      = Some(s"$key-switch"),
                url     = s"${langToCall(key)}",
                value   = Some(key.capitalize)
            ).toHtml
        } else {
           @key.capitalize
        }
        @if(key != langMap.last._1) { @Html(" | ") }
}
</p>

Page containing the switch

@import uk.my-org.exampleplay25.controllers.LanguageController
@import play.api.Application
@import play.api.i18n.Messages

@()(implicit request: Request[_], application: Application, messages: Messages, lang: Lang, messagesApi: MessagesApi)

<h1 id="message">Hello from example-play-25-frontend !</h1>
<h1>@Messages("test.message")</h1>


@clc = @{ Application.instanceCache[LanguageController].apply(application) }

@uk.my-org.exampleplay25.views.html.language_selection(clc.languageMap, clc.langToCall, Some("custom-class"))

The SwitchingController

package uk.my-org.exampleplay25.controllers

import javax.inject.Inject

import play.api.Play
import play.api.i18n._
import play.api.mvc._
import uk.my-org.play.config.RunMode

class LanguageController @Inject()(implicit val messagesApi: MessagesApi, lang: Langs) extends Controller with I18nSupport with RunMode     {
    protected def fallbackURL: String = Play.current.configuration.getString(s"$env.language.fallbackUrl").getOrElse("/")

    def languageMap: Map[String, Lang] = Map(
        "english" -> Lang("en"),
        "cymraeg" -> Lang("cy-GB"),
        "french" -> Lang("fr")
    )

    def langToCall(lang: String): Call = routes.LanguageController.switchToLanguage(lang)

    def switchToLanguage(language: String) = Action { implicit request =>
    val lang = languageMap.getOrElse(language, LanguageUtils.getCurrentLang)
    val redirectURL = request.headers.get(REFERER).getOrElse(fallbackURL)
    Redirect(redirectURL).withLang(Lang.apply(lang.code)).flashing(LanguageUtils.FlashWithSwitchIndicator)
    }
}

Application.conf

play.i18n.langs = [ "en", "cy", "fr" ]

App routes

GET        /hello-world             @uk.my-org.exampleplay25.controllers.HelloWorld.helloWorld

GET        /language/:lang          @uk.my-org.exampleplay25.controllers.LanguageController.switchToLanguage(lang: String)

Hello World controller

package uk.my-org.exampleplay25.controllers

import javax.inject.{Inject, Singleton}

import play.api.Play.current
import play.api.i18n.{I18nSupport, MessagesApi}
import play.api.mvc._
import uk.my-org.play.frontend.controller.FrontendController

import scala.concurrent.Future

@Singleton
class HelloWorld @Inject()(implicit val messagesApi: MessagesApi) extends FrontendController with I18nSupport {
    val helloWorld = Action.async { implicit request =>
            Future.successful(Ok(uk.my-org.exampleplay25.views.html.helloworld.hello_world()))
    }
}

Can anyone see what I'm doing wrong??

1
It's too much code and the issue hasn't really been narrowed down at all. I suggest you first migrate to play 2.4 and check if that works. Then migrate to 2.5. If that doesn't work, try to narrow down the problem and try to identify at which point the code no longer behaves as expected. - rethab

1 Answers

2
votes

The short answer is you're not declaring cy-GB as a language but you're trying to switch to it.

The long answer is you're over-complicating things. Here's an example to demonstrate language switching with minimal code.

The template

This will display a message from the relevant messages file as well as showing the lang derived from the request header.

@()(implicit messages: Messages, lang: Lang)

<h3>@messages("greeting")</h3>

<p>Current = @lang</p>
<ul>
  <li><a href="@routes.LanguageController.switchToLanguage("en")">English</a></li>
  <li><a href="@routes.LanguageController.switchToLanguage("cy")">Welsh</a></li>
</ul>

The configuration

Declare the languages you want to support:

play.i18n.langs = [ "en", "cy" ]

The application controller

The only special thing here is the I18nSupport trait.

package controllers

import javax.inject.Inject

import play.api.i18n.{I18nSupport, MessagesApi}
import play.api.mvc.{Action, Controller}
import views.html.index

import scala.concurrent.{ExecutionContext, Future}

class FooController @Inject()(val messagesApi: MessagesApi,
                              exec: ExecutionContext) extends Controller with I18nSupport {

  def home = Action.async { implicit request =>
    Future {Ok(index())}(exec)
  }
}

Changing the language

Again, nothing special and no additional support required.

package controllers

import javax.inject.Inject

import play.api.i18n._
import play.api.mvc._

class LanguageController @Inject()(val messagesApi: MessagesApi) extends Controller with I18nSupport {

  def switchToLanguage(language: String) = Action { implicit request =>
    Redirect(routes.FooController.home()).withLang(Lang(language))
  }
}

The translations

conf/messages.en

greeting=Good morning

conf/messages.cy

greeting=Bore da

routes

GET     /                    controllers.FooController.home
GET     /lang/:lang          controllers.LanguageController.switchToLanguage(lang: String)

That's everything you need

Running this will initially display in the language your browser is using. Clicking a link will switch to the selected language.

enter image description here

enter image description here