UTF-8 baştan sona


1191

Yeni bir sunucu kuruyorum ve web uygulamamda UTF-8'i tamamen desteklemek istiyorum. Ben geçmişte mevcut sunucular üzerinde denedim ve her zaman geri ISO-8859-1 düşmek zorunda gibi görünüyor.

Kodlama / karakter kümelerini tam olarak nerede ayarlamam gerekir? Bunu yapmak için Apache, MySQL ve PHP'yi yapılandırmam gerektiğini biliyorum - takip edebileceğim veya belki de uyumsuzlukların nerede meydana gelebileceğini giderebileceğim bazı standart kontrol listesi var mı?

Bu, MySQL 5, PHP, 5 ve Apache 2 çalıştıran yeni bir Linux sunucusu içindir.


8
İşte yapabileceğiniz tüm kodlama hataları hakkında bir genel bakış: sebastianviereck.de/en/…
Sebastian Viereck

13
İşte genel olarak kodlamalar ve özellikle PHP kodlamaları için bir giriş: Her Programcı Kesinlikle Ne
Olur,

PHP 7 hakkında son zamanlarda yapılan bazı tartışmalar , 2010'un "resmi olarak terk edilmiş" konumunda hiçbir değişiklik olmadığını gösteriyor ... "PHP7 ve UTF-8" hakkında daha fazla bir şey var mı?
Peter Krauss

Bu sorun yaygındır. Ancak bir kısayol çözümü yok, utf-8her biri için ayrı ayrı ayarlamanız gerekecek - MySQL 5, PHP 5 VEYA Apache 2.
Manish Shrivastava

Yanıtlar:


1015

Veri Depolama :

  • utf8mb4Veritabanınızdaki tüm tablolarda ve metin sütunlarında karakter kümesini belirtin . Bu, MySQL'in UTF-8'de yerel olarak kodlanmış değerleri fiziksel olarak depolamasını ve almasını sağlar. utf8mb4Bir utf8mb4_*harmanlama belirtilirse (açık karakter kümesi olmadan) MySQL'in örtülü olarak kodlama kullanacağını unutmayın .

  • MySQL'in eski sürümlerinde (<5.5.3) maalesef sadece utf8Unicode karakterlerin bir alt kümesini destekleyen basitçe kullanmak zorunda kalacaksınız . Keşke şaka yapsaydım.

Veri Erişimi :

  • Uygulama kodunuzda (örn. PHP), hangi DB erişim yönteminde kullanırsanız kullanın, bağlantı karakter setini ayarlamanız gerekir utf8mb4. Bu şekilde, MySQL, uygulamanıza veri gönderdiğinde yerel UTF-8'den dönüşüm yapmaz ve bunun tersi de geçerlidir.

  • Bazı sürücüler, hem kendi dahili durumunu güncelleyen hem de MySQL'i bağlantıda kullanılacak kodlama hakkında bilgilendiren bağlantı karakter kümesini yapılandırmak için kendi mekanizmasını sağlar; bu genellikle tercih edilen yaklaşımdır. PHP dilinde:

    • Eğer kullanıyorsanız PDO PHP ≥ 5.3.6 ile soyutlama katmanı şunları belirtebilirsiniz charsetiçinde DSN :

      $dbh = new PDO('mysql:charset=utf8mb4');
    • MySQLi kullanıyorsanız şunları arayabilirsiniz set_charset():

      $mysqli->set_charset('utf8mb4');       // object oriented style
      mysqli_set_charset($link, 'utf8mb4');  // procedural style
      
    • Düz mysql ile sıkışıp kalırsanız, PHP ≥ 5.2.3 çalıştırıyorsanız, arayabilirsiniz mysql_set_charset.

  • Sürücü bağlantısı karakter kümesini ayarlamak için kendi mekanizmasını sağlamıyorsa, başvurunuz bağlantısında veri kodlanmış olmasını bekler nasıl MySQL anlatmak için bir sorgu vermek gerekebilir: SET NAMES 'utf8mb4'.

  • Yukarıdaki ile aynı husus utf8mb4/ utf8yukarıdaki geçerlidir.

Çıktı :

  • Uygulamanız metni başka sistemlere aktarıyorsa, karakter kodlaması hakkında da bilgilendirilmeleri gerekir. Web uygulamalarında, tarayıcıya verilerin gönderildiği kodlama hakkında bilgi verilmelidir (HTTP yanıt başlıkları veya HTML meta verileri aracılığıyla ).

  • PHP'de, default_charsetphp.ini seçeneğini kullanabilir veya Content-TypeMIME üstbilgisini kendiniz verebilirsiniz ; bu yalnızca daha fazla iştir ancak aynı etkiye sahiptir.

  • Çıkışı kullanarak kodlarken, ikinci bir parametre olarak json_encode()ekleyin JSON_UNESCAPED_UNICODE.

Giriş :

  • Ne yazık ki, herhangi bir yerde saklamaya veya kullanmaya çalışmadan önce alınan her dizeyi geçerli UTF-8 olarak doğrulamanız gerekir. PHP mb_check_encoding()hile yapar, ancak dini olarak kullanmak zorunda. Kötü niyetli istemciler istedikleri kodlamada veri gönderebildikleri için bunun hiçbir yolu yoktur ve PHP'nin bunu sizin için güvenilir bir şekilde yapmasını sağlamak için bir hile bulamadım.

  • Mevcut HTML spesifikasyonunu okuduğumda , aşağıdaki alt madde işaretleri modern HTML için artık gerekli değil hatta geçerli değil. Anladığım kadarıyla tarayıcılar belge için belirtilen karakter kümesinde çalışacak ve veri gönderecekler. Ancak, HTML'nin eski sürümlerini (XHTML, HTML4 vb.) Hedefliyorsanız, bu noktalar yine de yararlı olabilir:

    • Yalnızca HTML5'ten önceki HTML için : tarayıcılar tarafından size gönderilen tüm verilerin UTF-8'de olmasını istiyorsunuz. Eğer güvenilir bir şekilde yapmanın tek yolu gitmek yazık ki, bu eklemektir accept-charsettüm özniteligini <form>etiketleri: <form ... accept-charset="UTF-8">.
    • Yalnızca HTML5'ten önceki HTML için : W3C HTML spesifikasyonunun, istemcilerin sunucunun sunduğu karakter kümesinde formları sunucuya geri göndermek için "varsayılan" olması gerektiğini, ancak görünüşe göre yalnızca bir öneri olduğunu ve bu nedenle her birinde açık olması gerektiğini söyledi <form>etiket.

Diğer Kod Konuları :

  • Açıkçası, sunacağınız tüm dosyalar (PHP, HTML, JavaScript, vb.) Geçerli UTF-8 olarak kodlanmalıdır.

  • Bir UTF-8 dizesini her işlediğinizde, bunu güvenli bir şekilde yaptığınızdan emin olmanız gerekir. Maalesef bu zor kısım. Muhtemelen PHP'nin mbstringuzantısını kapsamlı bir şekilde kullanmak isteyeceksiniz .

  • PHP dize operasyonları yerleşik olan değil varsayılan UTF-8 kasa ile. Normal PHP dize işlemleri (birleştirme gibi) ile güvenle yapabileceğiniz bazı şeyler vardır, ancak çoğu şey için eşdeğer mbstringişlevi kullanmalısınız .

  • Ne yaptığınızı bilmek (okuyun: berbat etmeyin), gerçekten UTF-8'i ve mümkün olan en düşük seviyede nasıl çalıştığını bilmeniz gerekir. Bilmeniz gereken her şeyi öğrenmek için bazı iyi kaynaklar için utf8.com'daki bağlantılardan herhangi birine göz atın .


4
Harmanlamayı utf8_ * olarak belirtirseniz, otomatik olarak utf8 olarak da kodladığını anlıyorum. Bu yanlış mı?
chazomaticus

49
Yanlış değilim: COLLATE KARAKTER SETİ anlamına gelir. Bkz. Örneğin dev.mysql.com/doc/refman/5.0/en/charset-database.html .
chazomaticus

7
Karakter setini ayarlamak için PDO örnekleri de ekleyebilirsiniz.
Ja͢ck

97
MySQL'in herkesle aynı dili konuşmadığını unutmayın. MySQL "utf8" derken, gerçekten "tanrı için üç bayt ile sınırlı olan tuhaf bir şekilde geciktirilmiş UTF-8 varyantı ne saçma bir sebep bilir" anlamına gelir. UTF-8'i gerçekten istiyorsanız, MySQL'e MySQL'in utf8mb4'ü aramaktan hoşlandığı bu garip şeyi istediğinizi söylemelisiniz . "WTF!" Ler üzerinde tasarruf zahmet etmeyin.
R. Martinho Fernandes

4
Bu cevap bana çok yardımcı oldu AMA ben de benim durumumda ben PHP json_encode ajax üzerinden DB sorgu sonuçları geçerken JSON_UNESCAPED_UNICODE eklemek için gerekli bulundu.
Petay87

150

Chazomaticus'un mükemmel cevabına bir şey eklemek istiyorum :

META etiketini de unutmayın (bunun gibi veya HTML4 veya XHTML sürümü ):

<meta charset="utf-8">

Bu önemsiz görünüyor, ama IE7 bana daha önce bununla ilgili sorunlar verdi.

Her şeyi doğru yapıyordum; veritabanı, veritabanı bağlantısı ve Content-Type HTTP üstbilgisi UTF-8 olarak ayarlanmıştı ve diğer tüm tarayıcılarda iyi çalıştı, ancak Internet Explorer hala "Batı Avrupa" kodlamasını kullanmakta ısrar etti.

Sayfada META etiketi eksik olduğu ortaya çıktı. Bunu eklemek sorunu çözdü.

Düzenle:

W3C'nin aslında I18N'ye adanmış oldukça büyük bir bölümü var . Bu konuyla ilgili bir dizi makaleleri var - HTTP, (X) HTML ve CSS tarafını açıklayan:

Hem HTTP üstbilgisini hem de HTML meta etiketini (veya XHTML'nin XML olarak sunulması durumunda XML bildirimini) kullanmanızı önerirler.


HTTP üstbilgilerinde karakter kümesini belirtmek mümkün olmamalı mıydı? Muhtemelen web sunucusu için bazı yapılandırma seçeneklerine ...
oliver

2
@oliver: Evet, HTTP üstbilgisinde gönderebilirsiniz, ancak içerik dosyayı göndermek daha iyidir, çünkü istemci dosyayı kaydederse, her zaman meta etiketi kaydeder. Bir HTTP üstbilgisi, tarayıcı kaydedilen dosyadaki bir meta etikete kopyalamak için yeterince akıllı olmadıkça muhtemelen kaybolur.

5
Ayrıca, satırın başlık öğesinin ilk alt öğesi olduğundan emin olun (herhangi bir Unicode öğesinden önce). Tarayıcı, yukarıda açıklanan meta öğeye isabet ettikten sonra sayfayı yeniden yorumlayabilir.
alex

64

default_charsetPhp.ini içinde ayarlamaya ek olarak header(), herhangi bir çıktıdan önce kodunuzdan doğru karakter setini gönderebilirsiniz :

header('Content-Type: text/html; charset=utf-8');

Dize işlevlerinin çoğunun Unicode ile çalışmadığını ve bazılarının dizeleri tamamen değiştirebileceğini bildiğiniz sürece Unicode ile PHP'de çalışmak kolaydır . PHP "karakterleri" 1 bayt uzunluğunda görür. Bazen bu iyidir (örneğin, explode()sadece bir bayt dizisi arar ve ayırıcı olarak kullanır - bu nedenle hangi gerçek karakterleri aradığınız önemli değildir). Ancak diğer zamanlarda, işlev aslında karakterler üzerinde çalışmak üzere tasarlandığında , PHP'nin metninizin Unicode ile bulunan çok baytlı karakterleri olduğu hakkında hiçbir fikri yoktur.

Kontrol etmek için iyi bir kütüphane phputf8 . Bu, UTF8 dizelerinde güvenli bir şekilde çalışabilmeniz için tüm "kötü" işlevleri yeniden yazar. Mbstring uzantısı gibi bunu sizin için de yapmaya çalışan uzantılar var, ancak daha taşınabilir olduğu için kütüphaneyi kullanmayı tercih ediyorum (ancak kitle pazarı ürünleri yazıyorum, bu benim için önemli). Ancak phputf8, performansı artırmak için yine de perde arkasında mbstring kullanabilir.


Php.ini dosyasındaki aşırı yük ayarını yapın. Çok baytlı dizeler kullanırken yardımcı olur.
Anthony Rutledge

32

PDO kullanan biriyle ilgili bir sorun buldum ve cevap PDO bağlantı dizesi için kullanmak oldu:

$pdo = new PDO(
    'mysql:host=mysql.example.com;dbname=example_db',
    "username",
    "password",
    array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

Bunu aldığım site çalışmıyor, ancak neyse ki Google önbelleğini kullanarak alabildim.


1
Biraz daha ileriye baktığımızda, bu sadece 5.3.6'dan önceki PHP sürümleri için gereklidir. Ayrıca bkz: http://stackoverflow.com/a/4361485/2286722 (her ne kadar ayrı kullanıyorlarsa $dbh->exec("set names utf8");; burada sunulan yöntemi tercih ederim). Btw. php.net/manual/en/pdo.construct.php#96325 : PHP kılavuzunda bir yorum olarak da buna benzer bir not var .
Marten Koetsier


24

Benim durumumda, mb_splitregex kullanan kullanıyordum. Bu nedenle ben de regex kodlama utf-8 olduğundan emin olmak zorunda kaldımb_regex_encoding('UTF-8');

Bir yan not olarak, mb_internal_encoding()dahili kodlamanın utf-8 olmadığını çalıştırarak da keşfettim ve çalıştırarak değiştirdim mb_internal_encoding("UTF-8");.


22

Her şeyden önce <5.3PHP içinde iseniz o zaman hayır. Üstesinden gelmeniz gereken bir sürü problem var.

Unicode , graphemes , string işlemleri , yerelleştirme ve çok daha fazlası için iyi bir desteğe sahip olan intl kütüphanesinden hiç kimsenin bahsetmediğine şaşırdım .

Ben PHP 'unicode desteği hakkında bazı bilgiler teklif edecek Elizabeth Smith'in slaytlar de PHPBenelux'14

INTL

İyi:

  • YBÜ Kütüphanesi Çevresinde Sarıcı
  • Standart yerel ayarlar, komut dosyası başına yerel ayarı ayarla
  • Sayı biçimlendirme
  • Para birimi biçimlendirme
  • Mesaj biçimlendirme (gettext'in yerine geçer)
  • Takvimler, tarihler, saat dilimi ve saat
  • transliterator
  • Spoofchecker
  • Kaynak paketleri
  • Dönüştürücüler
  • IDN desteği
  • grafemler
  • karşılaştırma
  • yineleyiciler

Kötü:

  • Zend_multibite'yi desteklemiyor
  • HTTP giriş çıkış dönüşümünü desteklemiyor
  • Fonksiyon aşırı yüklemesini desteklemez

mb_string

  • Zend_multibyte desteğini etkinleştirir
  • Şeffaf HTTP giriş / çıkış kodlamasını destekler
  • Strtoupper gibi işlev için bazı sarıcılar sağlar

ICONV

  • Karakter seti dönüşümü için birincil
  • Çıktı tamponu işleyicisi
  • mime kodlama işlevi
  • dönüştürmek
  • bazı string yardımcıları (len, substr, strpos, strrpos)
  • Akış Filtresi stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')

VERİTABANLARI

  • mysql: Tablolarda ve bağlantıda karakter seti ve harmanlama (harmanlama değil). Ayrıca mysql - msqli veya PDO kullanmayın
  • postgresql: pg_set_client_encoding
  • sqlite (3): Unicode ve intl desteği ile derlendiğinden emin olun

Diğer bazı Gotchas

  • 3. bölüm uzantısı kullanmadığınız sürece PHP ve pencerelerle unicode dosya adlarını kullanamazsınız.
  • Exec, proc_open ve diğer komut satırı çağrılarını kullanıyorsanız ASCII'deki her şeyi gönderin
  • Düz metin düz metin değil, dosyaların kodlaması var
  • İconv filtresi ile dosyaları anında dönüştürebilirsiniz

Eklenen özelliklerin değişmesi durumunda bu cevabı güncelleyeceğim.


2
Evet doğru. Mysqli ve PDO kendi yerel sürücülerini kullanabilir. Ayrıca --with-mysqli=mysqlnd --with-pdo-mysql=mysqlndseçenekler ile php derlemek durumunda onlar mysqlnd sürücüsü kullanabilirsiniz .
Alexander Yancharuk

14

Bu şaşırtıcı cevaplara ekleyeceğim tek şey utf8 kodlama dosyalarınızı kaydetme vurgulamaktır, ben tarayıcılar bu özelliği kod kodlama olarak utf8 ayarı üzerinde kabul ettim. Herhangi bir iyi metin editörü bunu gösterecektir, örneğin Notepad ++ dosya koruma için bir menü seçeneğine sahiptir, size geçerli kodlamayı gösterir ve değiştirmenize olanak tanır. Tüm php dosyalarım için BOM olmadan utf8 kullanıyorum.

Bir zaman önce birisi başka biri tarafından tasarlanmış bir php / mysql uygulaması için utf8 desteği eklememi istedi, tüm dosyaların ANSI kodlandığını fark ettim, bu yüzden tüm dosyaları dönüştürmek için ICONV kullanmak zorunda kaldım. utf8 charset ve utf8_general_ci harmanla, bağlantıdan sonra veritabanı soyutlama katmanına 'SET NAMES utf8' ekleyin (5.3.6 veya önceki bir sürümü kullanıyorsanız, bağlantı dizesinde charset = utf8 kullanmanız gerekir) ve php multibyte kullanmak için dize işlevlerini değiştirin dize işlevi eşdeğer.


13

Son zamanlarda strtolower(), özel bir karakterden sonra verilerin kesildiği durumlarda sorunlara neden olabileceğini keşfettim .

Çözüm,

mb_strtolower($string, 'UTF-8');

mb_, MultiByte kullanır. Daha fazla karakteri destekler, ancak genel olarak biraz daha yavaştır.


9

Aynı sorunu yaşadım ve PHP kılavuzlarında iyi bir çözüm buldum.

Tüm dosya kodlamamı UTF8 olarak değiştirdim ve ardından bağlantımdaki varsayılan kodlamayı değiştirdim. Bu tüm sorunları çözdü.

if (!$mysqli->set_charset("utf8")) {
    printf("Error loading character set utf8: %s\n", $mysqli->error);
} else {
   printf("Current character set: %s\n", $mysqli->character_set_name());
}

Kaynağı Görüntüle


2
Üzerinde çalıştığım bir sayfada kodlama sorunu bulmaya bir saat harcadım ve genellikle bir şeyler bulmakta oldukça iyiyim. Her zaman bu sayfaya başvuruyorum ve cevabınız bana çok yardımcı oldu. Benim oyumu aldım. Benim durumumda, set_charset('utf8mb4')işe yaramadı ama işe >set_charset("utf8")yaradı ve bu aslında diğer cevaplarda gösterilmedi.
Funk Forty Niner

Dikkat @FunkFortyNiner: set_charset("utf8")çalışabilir ancak farklı davranır (arasındaki fark hakkında açıklamalar göreceksiniz utf8ve utf8mb4ve MySQL sürüm geçmişi). Kullanım utf8 size varsa VE SADECE sen ne yaptığınızı biliyorsanız !
Martin Hennings

5 yıldızlı çözüm, satır satır bir metin dosyası okuyup alıyordum? her karakter için, o zaman ansi yerine utf8 kullandığı gibi kaydettim. Teşekkürler.
Atef Farouk

8

PHP'de, çokbaytlı işlevleri kullanmanız veya mbstring.func_overload'u açmanız gerekir . Bu şekilde, birden fazla bayt alan karakterleriniz varsa strlen gibi şeyler işe yarayacaktır.

Ayrıca, yanıtlarınızın karakter kümesini de tanımlamanız gerekir. Yukarıdaki gibi AddDefaultCharset'i kullanabilir veya üstbilgiyi döndüren PHP kodu yazabilirsiniz. (Veya HTML belgelerinize META etiketi ekleyebilirsiniz.)


Func_overload ayarı hakkında mükemmel ipucu - mevcut kodda minimum değişiklik yapılmasına olanak tanır.
Simon East

4
Dikkatli olun - bazı kodlar aslında standart dize işlevlerinin karakter başına bir baytlık doğasına güveniyor olabilir.
JW.

Mbstring.func_overload özelliğinin yukarıdaki @ JW'nin açıklamasında belirtilen sorunlar nedeniyle PHP 7.2'den itibaren kullanımdan kaldırıldığını belirtmek önemlidir. Bu yüzden en iyi tavsiye şudur: Evet, mbstring işlevlerini kesinlikle kullanmalısınız, ancak standart işlevlerin çok baytlı çalışmasını sağlamak için aşırı yük özelliğini kullanmayın.
Simba

6

PHP'de Unicode desteği hala büyük bir karmaşa. ISO8859 dizesini (dahili olarak kullandığı) utf8'e dönüştürme yeteneğine sahip olsa da, yerel olarak unicode dizelerle çalışma yeteneğinden yoksundur, bu da tüm dize işleme işlevlerinin dizelerinizi değiştirip bozacağı anlamına gelir. Bu nedenle uygun utf8 desteği için ayrı bir kitaplık kullanmanız veya tüm dize işleme işlevlerini kendiniz yeniden yazmanız gerekir.

Kolay kısım sadece HTTP üstbilgilerinde ve veritabanında karakter kümesini belirtmektir, ancak PHP kodunuz geçerli UTF8 çıktısı vermezse bunların hiçbiri önemli değildir. Bu zor kısmı ve PHP size neredeyse hiç yardım etmiyor. (Bence PHP6 bunun en kötüsünü düzeltmesi gerekiyor, ama yine de bir süre uzakta)


6

Eğer MySQL sunucusu bir istemci olarak karakter kümesini değil, PHP karar vermek istiyorsanız (eski davranış; bence, tercih) eklemeyi deneyin skip-character-set-client-handshakesizin için my.cnf, altında [mysqld]ve yeniden başlatma mysql.

Bu, UTF8 dışında bir şey kullanmanız durumunda sorunlara neden olabilir.


5

En iyi cevap mükemmel. İşte düzenli bir debian / php / mysql kurulumunda ne vardı:

// storage
// debian. apparently already utf-8

// retrieval
// the mysql database was stored in utf-8, 
// but apparently php was requesting iso. this worked: 
// ***notice "utf8", without dash, this is a mysql encoding***
mysql_set_charset('utf8');

// delivery
// php.ini did not have a default charset, 
// (it was commented out, shared host) and
// no http encoding was specified in the apache headers.
// this made apache send out a utf-8 header
// (and perhaps made php actually send out utf-8)
// ***notice "utf-8", with dash, this is a php encoding***
ini_set('default_charset','utf-8');

// submission
// this worked in all major browsers once apache
// was sending out the utf-8 header. i didnt add
// the accept-charset attribute.

// processing
// changed a few commands in php, like substr,
// to mb_substr

hepsi buydu !


1

bir mysql çözümü istiyorsanız, bir sunucu geçişinden sonra 2 projemle benzer sorunlar yaşadım. Arama ve bir sürü çözüm denedikten sonra ben bu bir / bu işe yaramadan önce hiçbir şey ile karşılaştım):

mysqli_set_charset($con,"utf8");

Bu satırı benim yapılandırma dosyasına ekledikten sonra her şey yolunda gidiyor!

Html sorgusundan bir ek çözmek için çalışırken bu çözümü https://www.w3schools.com/PHP/func_mysqli_set_charset.asp buldum

iyi şanslar!


1

Sadece bir not:

Eğer sigara latin karakterlerin sorunu olarak gösteren karşı karşıya ?????????, bir soru soruldu ve bu kanonik soruya atfen kapalı var, her şeyi denedik ve size ne hala olursa olsun almak ??????????den MySQL.

Bunun nedeni , veritabanına yanlış karakter kümesi kullanılarak eklenen ve aslında soru işareti karakterlerine dönüştürülen ve depolanan eski verilerinizi test etmenizdir ?. Bu, orijinal metninizi sonsuza kadar kaybettiğiniz anlamına gelir ve ne denerseniz deneyin, alırsınız ???????.

bu sorunun cevaplarından öğrendiklerinizi yeni bir veriye uygulamak sorununuzu çözebilir.


0

Tabloları görüntülerken bu sorunu yaşadım. Ben sadece her yankı çıktı değişkeni koymak:

<td><?php echo utf8_encode ($Local) ?></td>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.