CSS: преобразование текста не работает должным образом для турецких символов

Реализации основных браузеров, похоже, имеют проблемы с text-transform: uppercase с турецкими символами. Насколько я знаю (я не турок), существует четыре разных символа i: ı i I İ, где последние два представляют собой прописные буквы первых двух.

Однако, применяя text-transform:uppercase к ı i, браузеры (проверено IE, Firefox, Chrome и Safari) приводят к I I, что неверно и может изменить значение слов настолько, что они станут оскорблениями. (Это то, что мне сказали)

Поскольку мои поиски решений не выявили, мой вопрос: есть ли обходные пути для этой проблемы? Первым обходным решением может быть полное удаление text-transform: uppercase, но это своего рода последнее средство.

Забавно, что у W3C есть тесты для этой проблемы на их сайте, но нет дополнительной информации об этой проблеме. http://www.w3.org/International/tests/tests-html-css/tests-text-transform/generate?test=5

Я ценю любую помощь и с нетерпением жду ваших ответов :-)

Вот codepen


person Malax    schedule 23.09.2010    source источник
comment
Можете ли вы показать рабочий, эээ, нерабочий пример вашего случая?   -  person Tim    schedule 27.09.2010
comment
Ссылка больше не работает, к вашему сведению.   -  person Ahmet Alp Balkan    schedule 22.11.2012


Ответы (8)


Вы можете добавить атрибут lang и установить для него значение tr, чтобы решить эту проблему:

<html lang="tr"> or <div lang="tr">

Вот рабочий пример.

person Hkan    schedule 24.03.2014
comment
У меня проблемы с этим. Он отлично работает на рабочем столе как с Chrome, так и с Safari. Однако браузеры iOS, похоже, игнорируют этот тег. Он не работает в мобильном Chrome и мобильном Safari, есть идеи? - person gok; 18.06.2015
comment
ок, судя по всему, проблема с iOS 7~, работает на iOS 8~ - person gok; 18.06.2015
comment
Да, я только что протестировал Chrome и Safari на iOS 8, и все в порядке. - person Hkan; 18.06.2015
comment
Спасибо за редактирование @Barlas. Недавно я узнал, что атрибут lang работает с любым элементом, но мне не пришло в голову отредактировать ответ. - person Hkan; 20.11.2015
comment
@Hkan np mate, я много раз сталкивался с этой ошибкой, вот решение js для этого: stackoverflow.com/a/33856951/ 1428241 - person Barlas Apaydin; 22.11.2015
comment
Этот ответ должен быть награжден. - person Melih; 29.06.2017

Вот пример быстрого и грязного обходного пути - это быстрее, чем я думал (проверено в документе с 2400 тегами -> без задержки). Но я вижу, что обходные пути js — не самое лучшее решение.

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-3">
</head>
<body>
<div style="text-transform:uppercase">a b c ç d e f g ğ h ı i j k l m n o ö p r s ş t u ü v y z (source)</div> <div>A B C Ç D E F G Ğ H I İ J K L M N O Ö P R S Ş T U Ü V Y Z (should be like this)</div>

<script>
    function getStyle(element, style) {
        var result;

        if (document.defaultView && document.defaultView.getComputedStyle) {
            result = document.defaultView.getComputedStyle(element, '').getPropertyValue(style);
        } else if(element.currentStyle) {
            style = style.replace(/\-(\w)/g, function (strMatch, p1) {
                return p1.toUpperCase();
            });
            result = element.currentStyle[style];
        }
        return result;
    }

    function replaceRecursive(element) {
        if (element && element.style && getStyle(element, 'text-transform') == 'uppercase') {
            element.innerHTML = element.innerHTML.replace(/ı/g, 'I');
            element.innerHTML = element.innerHTML.replace(/i/g, 'İ');    // replaces 'i' in tags too, regular expression should be extended if necessary
        }

        if (!element.childNodes || element.childNodes.length == 0) return;

        for (var n in element.childNodes) {
            replaceRecursive(element.childNodes[n]);
        }
    }

    window.onload = function() {    // as appropriate 'ondomready'
        alert('before...');
        replaceRecursive(document.getElementsByTagName('body')[0]);
        alert('...after');
    }
</script>

</body>
</html>
person alex    schedule 01.10.2010
comment
Мне нравится ваша реализация просто заглавной буквы всего содержимого, а не замены определенных символов и опоры на CSS, как я предложил. У меня есть вопрос о вашей рекурсивной замене и .innerHTML, в основном потому, что я недостаточно хорошо понимаю этот атрибут. Если у меня есть вложенные элементы ‹div id=a1› ‹div id=a2›contents‹/div›‹/div›, и я вызываю ваш replaceRecursive(), будут ли идентификаторы в верхнем регистре? Спасибо, что помогли мне понять вашу реализацию. - person Brian Stinar; 01.10.2010
comment
Возможно, вы захотите добавить тестирование для lang="tr" и определенно не должны использовать for...in для перебора NodeList объектов: developer .mozilla.org/En/DOM/NodeList. В противном случае +1 - person Yi Jiang; 03.10.2010
comment
Хорошие моменты Yi, также это не работает со смешанными дочерними узлами элемента/текста (например, когда у вас есть метка, обертывающая поле ввода и его описание). Я исправил все эти проблемы, и у меня есть решение, которое я использую в производстве, которым я поделюсь в качестве отдельного ответа. - person gtd; 05.01.2012

Вот моя расширенная версия кода Алекса, которую я использую в производстве:

(function($) {
  function getStyle(element, style) {
    var result;

    if (document.defaultView && document.defaultView.getComputedStyle) {
      result = document.defaultView.getComputedStyle(element, '').getPropertyValue(style);
    } else if(element.currentStyle) {
      style = style.replace(/\-(\w)/g, function (strMatch, p1) {
        return p1.toUpperCase();
      });
      result = element.currentStyle[style];
    }
    return result;
  }

  function replaceRecursive(element, lang) {
    if(element.lang) {
      lang = element.lang; // Maintain language context
    }

    if (element && element.style && getStyle(element, 'text-transform') == 'uppercase') {
      if (lang == 'tr' && element.value) {
        element.value = element.value.replace(/ı/g, 'I');
        element.value = element.value.replace(/i/g, 'İ');
      }

      for (var i = 0; i < element.childNodes.length; ++i) {
        if (lang == 'tr' && element.childNodes[i].nodeType == Node.TEXT_NODE) {
          element.childNodes[i].textContent = element.childNodes[i].textContent.replace(/ı/g, 'I');
          element.childNodes[i].textContent = element.childNodes[i].textContent.replace(/i/g, 'İ');
        } else {
          replaceRecursive(element.childNodes[i], lang);
        }
      }
    } else {
      if (!element.childNodes || element.childNodes.length == 0) return;

      for (var i = 0; i < element.childNodes.length; ++i) {
        replaceRecursive(element.childNodes[i], lang);
      }
    }
  }

  $(document).ready(function(){ replaceRecursive(document.getElementsByTagName('html')[0], ''); })
})(jQuery);

Обратите внимание, что здесь я использую jQuery только для функции ready(). Оболочка совместимости jQuery также является удобным способом пространства имен функций. Кроме того, эти две функции вообще не зависят от jQuery, так что вы можете их вытащить.

По сравнению с оригинальной версией Алекса эта решает пару проблем:

  • Он отслеживает атрибут lang по мере его рекурсии, поскольку, если вы смешали турецкий и другой латинский контент, вы получите неправильные преобразования для нетурецкого без него. В соответствии с этим я передаю базовый элемент html, а не body. Вы можете прикрепить lang="en" к любому тегу, который не является турецким, чтобы предотвратить неправильное использование заглавных букв.

  • Он применяет преобразование только к TEXT_NODES, потому что предыдущий метод innerHTML не работал со смешанными узлами текста/элемента, такими как метки с текстом и флажками внутри них.

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

person gtd    schedule 05.01.2012
comment
Спасибо, что спросили об этом за год до того, как он мне понадобился :) - person gtd; 06.01.2012

В следующей версии Firefox Nightly (которая должна стать Firefox 14) исправлена ​​эта проблема, и она должна решать эту проблему без каких-либо взломов (как того требуют спецификации CSS3).

Кровавые подробности доступны в этой ошибке: https://bugzilla.mozilla.org/show_bug.cgi?id=231162

Я думаю, они также исправили проблему для варианта шрифта (для тех, кто не знает, что делает вариант шрифта, см. https://developer.mozilla.org/en/CSS/font-variant, еще не обновленный с учетом изменения, но документ не зависит от браузера и является вики, так что...)

person teoli    schedule 29.03.2012

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

Кто-то должен сообщить об этой проблеме разработчикам этих юникодных библиотек, и она будет исправлена ​​через несколько недель/месяцев.

person BarsMonster    schedule 28.09.2010
comment
Они не обращаются с ними неправильно, у них просто нет никакого способа узнать, что это должно быть турецким. - person tdammers; 01.10.2010
comment
недели/месяцы? Попробуйте годы/десятилетия. Я обнаружил открытые ошибки для Firefox и Safari: bugzilla.mozilla.org/show_bug.cgi ?id=231162 bugs.webkit.org/show_bug.cgi?id= 21312 - person gtd; 04.01.2012
comment
@tdammers Это назначение атрибута lang в HTML и заголовка Content-Language в HTTP. - person gtd; 05.01.2012

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

person Jakub Konecki    schedule 29.09.2010

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

Во-первых, определите, работаете ли вы на турецком языке. Если да, то отсканируйте все, что вы собираетесь перевести в верхний регистр, чтобы увидеть, содержит ли оно проблемные символы. Если это так, замените все эти символы на их версии в верхнем регистре. Затем примените CSS в верхнем регистре. Поскольку проблемные символы уже написаны в верхнем регистре, это должно быть совершенно нормальное (гетто) решение. Что касается Javascript, я предполагаю, что мне придется иметь дело с некоторыми .innerHTML на ваших затронутых элементах.

Дайте мне знать, если вам нужны какие-либо подробности реализации, у меня есть хорошее представление о том, как это сделать в Javascript, используя методы манипулирования строками Javascript. Эта общая идея должна помочь вам в этом (и, надеюсь, принесет мне награду!)

-Брайан Дж. Стинар-

person Brian Stinar    schedule 29.09.2010

Это не предпочтительный способ, но если у вас нет другого варианта: вы также можете решить это с помощью родного javascript:

Вот и его суть.

String.prototype.turkishToLower = function(){
  var string = this;
  var letters = { "İ": "i", "I": "ı", "Ş": "ş", "Ğ": "ğ", "Ü": "ü", "Ö": "ö", "Ç": "ç" };
  string = string.replace(/(([İIŞĞÜÇÖ]))/g, function(letter){ return letters[letter]; })
  return string.toLowerCase();
}

String.prototype.turkishToUpper = function(){
  var string = this;
  var letters = { "i": "İ", "ş": "Ş", "ğ": "Ğ", "ü": "Ü", "ö": "Ö", "ç": "Ç", "ı": "I" };
  string = string.replace(/(([iışğüçö]))/g, function(letter){ return letters[letter]; })
  return string.toUpperCase();
}

var text = 'iii';
text = text.turkishToUpper();
console.log(text);

person Barlas Apaydin    schedule 22.11.2015