Angular'da çoklu dil (i18n) nasıl yapılır?

Merhaba,


Angular'da çoklu dil desteği nasıl yapılır? Projelerimizde çoklu dili nasıl ekleyebiliriz? Bununla ilgili bir yöntem göstermek istiyorum.


Projemizi oluşturarak başlayalım;

ng new angularMultiLanguage --routing
cd angularMultiLanguage
ng serve


Hemen app.component.html dosyamızı açtık ve gereksiz kodları temizledikten sonra bir menü oluşturalım dil değiştirme işini de buradan yaparız;

<header>
  <ul class="left-menu">
    <li><a routerLink="/">Anasayfa</a></li>
    <li><a routerLink="campaigns">Kampanyalar</a></li>
    <li><a>Son eklenen ürünler</a></li>
    <li><a>Çok satan ürünler</a></li>
  </ul>
  <ul class="right-menu">
    <li><a><img src="https://lipis.github.io/flag-icon-css/flags/4x3/tr.svg"></a></li>
    <li><a><img src="https://lipis.github.io/flag-icon-css/flags/4x3/gb.svg"></a></li>
  </ul>
</header>
<router-outlet></router-outlet>


birazda şekil verelim, app.component.scss;

header {
  background-color: #f9f9f9;
  padding: 15px 0;
  display: flex;
  justify-content: space-between;
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
    li {
      display: inline-block;
      a {
        cursor: pointer;
        transition: .3s;
        img {
          width: 30px;
          margin: 0 5px;
        }
        &:hover {
          background-color: #f3f3f3;
        }
      }
    }
    &.left-menu {
      li {
        a {
          padding: 15px;
        }
      }
    }
  }
}


2 tane yeni component oluşturalım.

ng g c components/home
ng g c components/campaigns


app-routing.module.ts içerisinde routes değişkenini aşağıdaki gibi ayarlayalım;

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'campaigns', component: CampaignsComponent }
];



Şimdi çoklu dil kısmına giriş yapıyoruz.


assets klasörü içinde i18n adında bir klasör oluşturalım ve içerisine de i18n.json adında bir dosya oluşturalım. Siz isimleri istediğiniz gibi verebilirsiniz. i18.json dosyamızda çevirilerimizi tutacağız. Örneğin aşağıdaki gibi;

{
  "home": {
    "tr": "Anasayfa",
    "en": "Homepage"
  },
  "campaigns": {
    "tr": "Kampanyalar",
    "en": "Campaigns"
  }
}


Bir tane servis oluşturacağız adınada translate diyelim; (spec dosyası istemiyorsanız komutun sonuna --spec false yazınız.)

ng g s services/translate


Burada HttpClientModule'i kullanacağız bunun için app.module.ts dosyamıza gidip bu modülü import edelim. app.module.ts;

imports: [
  ...
  HttpClientModule
]


translate.service.ts;

import { Injectable } from '@angular/core';
import {HttpClient} from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class TranslateService {

  data: any = {};

  constructor(
    private http: HttpClient
  ) { }

  use(lang: string): Promise<{}> {
    return new Promise<{}>((resolve, reject) => {
      const langPath = `assets/i18n/i18n.json`;

      this.http.get<{}>(langPath).subscribe(
        translation => {
          // console.log(translation);
          Object.keys(translation).forEach((key) => {
            translation[key] = translation[key][lang];
          });
          this.data = Object.assign({}, translation || {});
          resolve(this.data);
        },
        error => {
          console.log(error);
          this.data = {};
          resolve(this.data);
        }
      );
    });
  }

}


Hemen sonrasında app.module.ts'yi açıyoruz ve @NgModule()'nin üstüne şu fonksiyonu oluşturuyoruz;

import {TranslateService} from './services/translate.service';

export function setupTranslateFactory(
  service: TranslateService): Function {
  return () => service.use('tr');
}

burada service.use('tr') kısmında varsayılan olarak hangi dili kullanacaksanız onun i18n.json dosyasındaki key olarak karşılığını yazıyorsunuz.

Bunu yaptıktan sonra da yine app.module.ts dosyasında providers bölümünde aşağıdaki tanımlamayı yapıyoruz;

import {APP_INITIALIZER, NgModule} from '@angular/core';

providers: [
  ...
  {
    provide: APP_INITIALIZER,
    useFactory: setupTranslateFactory,
    deps: [ TranslateService ],
    multi: true
  }
]


Buraya kadar bir sürü şey yaptık ama "aga bu nedir?" diye soruyor gibisiniz. Açıklayalım;

  1. i18n.json: bu dosyayı çevirilerimizi orada tanımlamak için oluşturduk. Orada değişken tanımlıyormuşuzda onun değerini çekiyormuşuz gibi düşünebilirsiniz. Örneğin home adında bir değişken oluşturdum ve bunun içerisinde "tr" ve "en" olarak değerlerini yazdım.
  2. TranslateService: Burada tanımladığımız data değişkeni bizim o an aktif olan dile ait değişkenleri ve buna ait kelimeleri, çevirileri tuttuğumuz değişkenimiz olacak. Bunuda use methodu ile yapacağız. use methodu aynı zamanda dil değiştirme işini halletmemiz için gerekiyor. Onun içerisinde yapılan işlemler ise kısaca i18n.json dosyamıza http.get isteği ile bağlanmak ve içerisindeki keyleri forEach ile döndürüp gönderdiğimiz dil parametresine göre içerideki değişkenlerin değerlerini almak ve bunları oluşturduğumuz data değişkenine atamak.
  3. setpTranslateFactory: app.module.ts içerisinde oluşturduğumuz bu fonksiyon ile projemiz açıldığında varsayılan olarak hangi dil kullanılacaksa onu TranslateService'indeki use methodumuza gönderiyoruz. Tabi bu fonksiyonu çalıştırmak içinde providers kısmında belirtiyoruz.


Evet buraya kadar yaptığımız kısımları anladık diye varsayıyorum ve geçelim bu yaptıklarımızı şimdi nasıl kullanacağız?


translate adında bir pipe oluşturalım; (spec dosyası istemiyorsanız komutun sonuna --spec false yazınız.)

ng g pipe pipes/translate


translate.pipe.ts;

import { Pipe, PipeTransform } from '@angular/core';
import {TranslateService} from '../services/translate.service';

@Pipe({
  name: 'translate',
  pure: false
})
export class TranslatePipe implements PipeTransform {

  constructor(
    private translate: TranslateService
  ) {
  }

  transform(key: any): any {
    return this.translate.data[key];
  }

}


Bu pipe'ın amacı: çevirisi olacak bir kelime veya cümlenin i18n.json içerisinde değişken karşılığı olacakya hani. işte o değişkenin adını alacak gönderecek ve TranslateService'e orada da data değişkeninden içinden cümlemizi getirecek.


Yapalım ve daha iyi anlayalım.

i18n.json dosyasında şu iki çeviriyi bi ekleyelim;

{
  "home": {
    "tr": "Anasayfa",
    "en": "Homepage"
  },
  "campaigns": {
    "tr": "Kampanyalar",
    "en": "Campaigns"
  }
}



Şimdi gelelim app.component.html dosyasını açalım ve menüdeki Anasayfa yazısını silelim onun yerine şunu yazalım;

{{ 'home' | translate }}


Şimdi bakalım çıktımızda bir değişiklik var mı?

Yok.


Süper, bu işi başarıyla tamamladık diyebiliriz. TranslatePipe'ına 'home' string ifadesini gönderdik. Ordanda gittik TranslateService içerisindeki data değişkenine. Orada şunu aradı: data.home, yani Anasayfa değeri dönmüş oldu.


Bunu doğrulamak için i18n.json içindeki "Anasayfa" yı değiştirin ve kaydettiğinizde çalıştığını göreceksiniz.


Şimdi birde dil değiştirmeyi deneyelim bakalım nasıl yapacağız;


Tahmin edeceğiniz gibi dil değiştirme bölümümüz hangi component içerisindeyse onun .ts dosyasını açıp TranslateService i constructor içerisinde tanımlayıp use methodunu kullanmak. Hemen yapalım ve öyle görelim;


Dil değiştirme bölümümüz app.component.html içerisinde olduğu için app.component.ts dosyasını açalım ve constructor'ı ekleyip TranslateService'i çağıralım;

import { Component } from '@angular/core';
import {TranslateService} from './services/translate.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'angularMultiLanguage';

  constructor(
    public translateService: TranslateService
  ) {
  }
}


app.component.html'i açalım dil değişikliğini yapabilmek için click eventini kullanalım. Yani şunları;

<li><a><img src="https://lipis.github.io/flag-icon-css/flags/4x3/tr.svg"></a></li>
<li><a><img src="https://lipis.github.io/flag-icon-css/flags/4x3/gb.svg"></a></li>

şunlarla değiştirelim;

<li><a (click)="translateService.use('tr')"><img src="https://lipis.github.io/flag-icon-css/flags/4x3/tr.svg"></a></li>
<li><a (click)="translateService.use('en')"><img src="https://lipis.github.io/flag-icon-css/flags/4x3/gb.svg"></a></li>


Evet bakalım çıktımız ne durumda;


Süper. O zaman diğer çevirileride ekleyelim ve bitirelim.


i18n.json dosyası son hali;

{
  "home": {
    "tr": "Anasayfa",
    "en": "Homepage"
  },
  "campaigns": {
    "tr": "Kampanyalar",
    "en": "Campaigns"
  },
  "recentlyAdded": {
    "tr": "Son eklenen ürünler",
    "en": "Recently added products"
  },
  "bestsellers": {
    "tr": "Çok satan ürünler",
    "en": "Best selling products"
  },
  "homeWorks": {
    "tr": "anasayfa çalışıyor!",
    "en": "home works!"
  },
  "campaignWorks": {
    "tr": "kampanyalar çalışıyor!",
    "en": "campaigns works!"
  }
}


app.component.html dosyası son hali;

<header>
  <ul class="left-menu">
    <li><a routerLink="/">{{ 'home' | translate }}</a></li>
    <li><a routerLink="campaigns">{{ 'campaigns' | translate }}</a></li>
    <li><a>{{ 'recentlyAdded' | translate }}</a></li>
    <li><a>{{ 'bestsellers' | translate }}</a></li>
  </ul>
  <ul class="right-menu">
    <li><a (click)="translateService.use('tr')"><img src="https://lipis.github.io/flag-icon-css/flags/4x3/tr.svg"></a></li>
    <li><a (click)="translateService.use('en')"><img src="https://lipis.github.io/flag-icon-css/flags/4x3/gb.svg"></a></li>
  </ul>
</header>
<router-outlet></router-outlet>


home.component.html dosyası son hali;

<p>
  {{ 'homeWorks' | translate }}
</p>


campaigns.component.html dosyası son hali;

<p>
  {{ 'campaignWorks' | translate }}
</p>



Son olarak kontrol edelim;

Bu kadar.


Umarım faydalı olmuştur. Projemizin bitmiş hali için: https://github.com/MehmetSert/angularMultiLanguage

Mehmet Sert

HTML, CSS, Javascript ve Angular konularında tecrübe edinmiş ve Bursa'da Frontend Developer olarak bir firmada görev almaktayım. Yeni teknolojilere meraklıyım, öğrendiklerimi uygulayarak ve anlatarak pekiştirmeyi seviyorum....

"Angular'da çoklu dil (i18n) nasıl yapılır?" için 7 yorum yapıldı.
H.G
Habib Göker 11 Haziran 2019

Mehmet bey merhabalar; Bu paylaşımınız için teşekkür ediyorum. birebir uyguladım yaptıklarınızı herhangi bir sorun yok. şöyle birşey yapabilirmiyiz. aşşağıdaki json dosyası sizinkinde farklı olarak ben tek tek yerine bir array dönderip onun içinde dönmek istiyorum. json dosyama baktığınızda nedemek istediğimiz anlayacaksınız. ben bunu componet.html içinde şöyle yazdım. menü bölümü örnek kullanım: <ul *ngFor="let menu of {{ 'header' | translate }} "> <li>{{menu.name}}</li> </ul> i18n.json dosyası { "header": { "tr": [ { "name": "Anasayfa" }, { "name": "Kurumsal", } ], "en": [ { "name": "Home", }, { "name": "Corporate" } ], } } not:angularda biraz yeniyim :(

H.G
Habib Göker 11 Haziran 2019

Bir önceki yorumumda *ngFor kullanımını şu şekilde değiştirdim üstekini yanlış yazmışım ama hala aynı hata dönüyor. yeni değişiklik = *ngFor'u linin içine aldım <ul> <li *ngFor="let menu of {{ 'header' | translate }} ">{{menu.name}}</li> </ul>

Mehmet Sert (Yazı sahibi)12 Haziran 2019

Merhaba Habib, Gönderdiğin koddaki mantık ile çalıştırabilirsin tabiki ancak sadece menü için ayrı bir kontrol koyman gerekir ve bir düzene bağlı değilde iki düzene göre çalıştırmak zorunda kalırsın. Bu yazıdaki mantığa göre bunu uyarlarsan daha kolay işin içinden çıkabileceğini düşünüyorum. Oda şu şekilde; *x.component.ts* mainMenu = ['homepage', 'about', 'references', 'contact']; *x.component.html* <ul> <li *ngFor="let menu of mainMenu">{{ menu | translate }}</li> </ul> *i18n.json* { "homepage": { "tr": "Anasayfa", "en": "Homepage" }, "about": { "tr": "Hakkımızda", "en": "About" }, "references": { "tr": "Referanslar", "en": "References" }, "contact": { "tr": "İletişim", "en": "Contact" } }

H.G
Habib Göker 14 Haziran 2019

Mehmet bey değerli cevabınız için çok teşekkür ederim. O gün akşam biraz kurcalarken şurada bir hata yaptığımı gördüm. eski *ngfor Bölümü <ul> <li *ngFor="let menu of {{ 'header' | translate }} ">{{menu.name}}</li> </ul> Bu üstteki yerde hatam şuymuş let menu of yazdıkdan sonra *ngFor içinde olduğum için 2 adet süslü parentez koymama gerek yokmuş yani şu şekilde güncelledim <ul> <li *ngFor="let menu of 'header' | translate ">{{menu.name}}</li> </ul> ve inanılmaz şekilde dillerin değişmesi ile tüm menü json dosyasından gelen array ile yeniden oluşuyor inanılmaz süper oldu. json dosyamı ve son header componet dosyamın içeriğini atıyorum başka arkadaşlar ihtiyacı olur ise ----*i18n.json*---- { "header": { "tr": [ { "name": "ANASAYFA", "rootName": "/" }, { "name": "KURUMSAL", "rootName": "/Kurumsal" }, { "name": "İLETİŞİM", "rootName": "/İletisim" } ], "en": [ { "name": "Home", "rootName": "/" }, { "name": "CORPORATE", "rootName": "/Kurumsal" }, { "name": "CONTACT", "rootName": "/İletisim" } ] } } ---*header.component.html*--- <ul class="main-nav nav navbar-nav"> <li *ngFor="let menu of 'header' | translate"><a [routerLinkActive]="'active'" [routerLinkActiveOptions]="{exact: true}" routerLink="{{menu.rootName}}">{{menu.name}}</a></li> </ul> Not: [routerLinkActive] , [routerLinkActiveOptions] ' bunlardan dolayı kafanız karışmasın arkadaşlar onların kullanımı başka birşey için oda [routerLinkActive] = linke tıklandıkdan sonra içine yazılan css o <li> element'in classına ekler bildiğimiz klasik active classı [routerLinkActiveOptions]="{exact: true}" = buda ana componet active kalıyordu sürekli bunu koyunca düzeldi. Angular CLI version: 7.3.9 Angular version: 7.2.15 kolay gelsin iyi çalışmalar

Mehmet Sert (Yazı sahibi)14 Haziran 2019

Evet *ngFor içerisinde süslü parantezler kullanılmaz senin ilk yorumunda ben onu farketmemiştim :) Sorunu çözmene çok sevindim ayrıca burada paylaştığın içinde teşekkür ederim.

H.G
Habib Göker 15 Haziran 2019

:) Bilmukabele

A.T
Ahmet Teke 08 Ağustos 2019

Paylasim icin cok tesekkurler.

Yorum yap * E-posta adresiniz yayınlanmayacak.