python - language_code - ¿Cómo hacer que Django slugify funcione correctamente con cadenas Unicode?
encoding django (7)
¿Qué puedo hacer para evitar que slugify
filter slugify
caracteres alfanuméricos no ASCII? (Estoy usando Django 1.0.2)
cnprog.com tiene caracteres chinos en las URL de las preguntas, así que busqué en su código. No están usando slugify
en las plantillas, sino que están llamando a este método en el modelo de Question
para obtener enlaces permanentes.
def get_absolute_url(self):
return ''%s%s'' % (reverse(''question'', args=[self.id]), self.title)
¿Están aplicando slugifying a las URL o no?
Además, la versión de Django de slugify no usa el indicador re.UNICODE, por lo que ni siquiera intentaría comprender el significado de /w/s
en lo que respecta a los caracteres no ascii.
Esta versión personalizada funciona bien para mí:
def u_slugify(txt):
"""A custom version of slugify that retains non-ascii characters. The purpose of this
function in the application is to make URLs more readable in a browser, so there are
some added heuristics to retain as much of the title meaning as possible while
excluding characters that are troublesome to read in URLs. For example, question marks
will be seen in the browser URL as %3F and are thereful unreadable. Although non-ascii
characters will also be hex-encoded in the raw URL, most browsers will display them
as human-readable glyphs in the address bar -- those should be kept in the slug."""
txt = txt.strip() # remove trailing whitespace
txt = re.sub(''/s*-/s*'',''-'', txt, re.UNICODE) # remove spaces before and after dashes
txt = re.sub(''[/s/]'', ''_'', txt, re.UNICODE) # replace remaining spaces with underscores
txt = re.sub(''(/d):(/d)'', r''/1-/2'', txt, re.UNICODE) # replace colons between numbers with dashes
txt = re.sub(''"'', "''", txt, re.UNICODE) # replace double quotes with single quotes
txt = re.sub(r''[?,:!@#~`+=$%^&//*()/[/]{}<>]'','''',txt, re.UNICODE) # remove some characters altogether
return txt
Tenga en cuenta la última sustitución de expresiones regulares. Esta es una solución a un problema con la expresión más robusta r''/W''
, que parece eliminar algunos caracteres que no son ASCII o recodificarlos incorrectamente, como se ilustra en la siguiente sesión de intérprete de Python:
Python 2.5.1 (r251:54863, Jun 17 2009, 20:37:34)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> # Paste in a non-ascii string (simplified Chinese), taken from http://globallives.org/wiki/152/
>>> str = ''您認識對全球社區感興趣的中國攝影師嗎''
>>> str
''/xe6/x82/xa8/xe8/xaa/x8d/xe8/xad/x98/xe5/xb0/x8d/xe5/x85/xa8/xe7/x90/x83/xe7/xa4/xbe/xe5/x8d/x80/xe6/x84/x9f/xe8/x88/x88/xe8/xb6/xa3/xe7/x9a/x84/xe4/xb8/xad/xe5/x9c/x8b/xe6/x94/x9d/xe5/xbd/xb1/xe5/xb8/xab/xe5/x97/x8e''
>>> print str
您認識對全球社區感興趣的中國攝影師嗎
>>> # Substitute all non-word characters with X
>>> re_str = re.sub(''/W'', ''X'', str, re.UNICODE)
>>> re_str
''XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/xa3/xe7/x9a/x84/xe4/xb8/xad/xe5/x9c/x8b/xe6/x94/x9d/xe5/xbd/xb1/xe5/xb8/xab/xe5/x97/x8e''
>>> print re_str
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX?的中國攝影師嗎
>>> # Notice above that it retained the last 7 glyphs, ostensibly because they are word characters
>>> # And where did that question mark come from?
>>>
>>>
>>> # Now do the same with only the last three glyphs of the string
>>> str = ''影師嗎''
>>> print str
影師嗎
>>> str
''/xe5/xbd/xb1/xe5/xb8/xab/xe5/x97/x8e''
>>> re.sub(''/W'',''X'',str,re.U)
''XXXXXXXXX''
>>> re.sub(''/W'',''X'',str)
''XXXXXXXXX''
>>> # Huh, now it seems to think those same characters are NOT word characters
No estoy seguro de cuál es el problema anterior, pero supongo que proviene de " lo que se clasifica como alfanumérico en la base de datos de propiedades de caracteres Unicode " y cómo se implementa. He oído que Python 3.x tiene una alta prioridad en el mejor manejo de Unicode, por lo que esto ya puede solucionarse. O tal vez es el comportamiento correcto de Python, y estoy haciendo un uso indebido de Unicode y / o del idioma chino.
Por ahora, una solución alternativa es evitar las clases de caracteres y realizar sustituciones basadas en conjuntos de caracteres explícitamente definidos.
Con Django> = 1.9 , django.utils.text.slugify
tiene un parámetro allow_unicode
:
>>> slugify("你好 World", allow_unicode=True)
"你好-world"
Si usa Django <= 1.8 (que no debería usar desde abril de 2018), puede obtener el código de Django 1.9 .
El equipo del sitio web de Mozilla ha estado trabajando en una implementación: https://github.com/mozilla/unicode-slugify código de ejemplo en http://davedash.com/2011/03/24/how-we-slug-at-mozilla/
Es posible que desee ver: https://github.com/un33k/django-uuslug
Cuidará ambas "U" para usted. U en único y U en Unicode.
Hará el trabajo sin problemas.
Esto es lo que uso:
http://trac.django-fr.org/browser/site/trunk/djangofr/links/slughifi.py
SlugHiFi es un contenedor para regular slugify, con la diferencia de que reemplaza los caracteres nacionales con sus contrapartes del alfabeto inglés.
Entonces, en lugar de "Ą", obtienes "A", en lugar de "Ł" => "L", y así sucesivamente.
Hay un paquete python llamado unidecode que he adoptado para el foro AskB Q & A, funciona bien para los alfabetos latinos e incluso parece razonable para el griego:
>>> import unidecode
>>> from unidecode import unidecode
>>> unidecode(u''διακριτικός'')
''diakritikos''
Hace algo extraño con los idiomas asiáticos:
>>> unidecode(u''影師嗎'')
''Ying Shi Ma ''
>>>
¿Esto tiene sentido?
En askbot calculamos babosas de esta manera:
from unidecode import unidecode
from django.template import defaultfilters
slug = defaultfilters.slugify(unidecode(input_text))
Me temo que la definición de django de slug significa ascii, aunque los documentos de django no lo mencionan explícitamente. Esta es la fuente de los filtros predeterminados para slugify ... puede ver que los valores se están convirtiendo a ascii, con la opción ''ignorar'' en caso de errores:
import unicodedata
value = unicodedata.normalize(''NFKD'', value).encode(''ascii'', ''ignore'')
value = unicode(re.sub(''[^/w/s-]'', '''', value).strip().lower())
return mark_safe(re.sub(''[-/s]+'', ''-'', value))
En función de eso, supongo que cnprog.com no está utilizando una función oficial de slugify
. Es posible que desee adaptar el fragmento de django anterior si desea un comportamiento diferente.
Habiendo dicho eso, el RFC para URLs declara que caracteres no-us-ascii (o, más específicamente, cualquier cosa que no sean los alfanuméricos y $ -_. +! * ''()) Deberían ser codificados usando la notación% hex. . Si nos fijamos en la solicitud GET en bruto real que envía su navegador (digamos, usando Firebug), verá que los caracteres chinos están de hecho codificados antes de ser enviados ... el navegador simplemente lo hace lucir bonito en la pantalla. Sospecho que esta es la razón por la cual slugify solo insiste en ascii, fwiw.