多语言 MySQL 内容:如果指定的语言不可用,如何选择给定的语言或另一种语言?

发布于 2024-11-05 14:34:11 字数 1146 浏览 2 评论 0原文

我正在开发一个多语言网站 PHP,并且希望获取给定语言的内容(如果可用)和另一种语言的内容(如果不可用)。我将在下面尝试解释我的问题。如果有不清楚的地方,请告诉我。

我的表格:

  • 内容content_idurl日期
  • content_l10ncontent_idl10n_id标题说明
  • l10nl10n_idname, order

第一种情况:

  • 我的访客说法语。
  • 我想要显示的内容有英语和法语两种版本。
  • 该网站应显示法语内容。

→ 易于使用 JOIN 实现。

第二种情况:

  • 我的访客说法语。
  • 我想要显示的内容仅提供英文
  • 网站应显示英文内容。

→ 如何实现?是否可以在单个请求中实现?

一些注意事项:

  • 这必须可以扩展到两种以上的语言。
  • 该解决方案必须适用于这两种情况,因为我不知道在执行请求之前内容是否以正确的语言提供。
  • 速度越快越好。
  • 如果需要,可以更改表格。
  • 我想选择多个 content 行(例如,对于标题列表)。
  • 有时,title 已翻译,但 descriptionNULL。理想情况下,请求会选择给定语言的标题,但会回退到另一种语言进行描述。
  • 最好设置后备的顺序(首先:给定语言,然后:按 order ASC 排序的 l10n 中的语言)。

我们将非常感谢您的帮助!先感谢您 !

亲切的问候,

奥利维尔

I'm developing a multilingual website PHP and would like to get content in a given language if available and in another one if not. I will try to explain my problem below. If something is not clear, please let me know.

My tables :

  • content : content_id, url, date
  • content_l10n : content_id, l10n_id, title, description
  • l10n : l10n_id, name, order

First case :

  • My visitor speaks French.
  • The content I want to display is available in both English and French.
  • The website should display the French content.

→ Easy to implement with a JOIN.

Second case :

  • My visitor speaks French.
  • The content I want to display is only available in English.
  • The website should display the English content.

→ How to implement that ? Is it possible in a single request ?

Some notes :

  • This has to be extensible to more than two languages.
  • The solution has to work with either cases, as I don't know if the content is available in the right language before doing the request.
  • The faster it is, the better it is.
  • The tables can be changed if needed.
  • I would like to select multiple content rows (for a list of titles, by example).
  • Sometimes, title is translated but description is NULL. Ideally, the request would select the title in the given language but would fallback to another language for the description.
  • It would be great to set the order of fallback (first : given language, then : languages from l10n ordered by order ASC).

Your help will be really appreciated ! Thank you in advance !

Kind regards,

olivier

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

帅气尐潴 2024-11-12 14:34:11

此解决方案适用于许多行,但每种语言需要 1 个 LEFT JOIN,并且 JOIn 的顺序指定优先级。

SELECT   c.url, c.date, 
         COALESCE( c1.title, c2.title ),
         COALESCE( c1.description, c2.description )
FROM      content c
LEFT JOIN content_l10n c1 ON (c1.content_id = c.content_id AND c1.l10n_id=$1)
LEFT JOIN content_l10n c2 ON (c2.content_id = c.content_id AND c2.l10n_id=$2)

(注意:我假设 $1 和 $2 是用户的前 2 种首选语言,并且它们缓存在会话中,因此不需要使用 l10n 进行额外的 JOIN)。

对于您的表结构,这是设置语言顺序的唯一有意义的方法。您需要一个额外的表来指定每个用户的语言首选项,而不是将订单存储在 l10n 表中。因此,假设您有一个表

user_l10n( user_id, l10n_id, order )

,并且假设表 l10n 保留其“order”字段作为默认值。

如果这样做:

SELECT   ..., COALESCE(ul.order,l.order) AS order
FROM      
          content      c
JOIN      content_l10n cl USING (content_id)
JOIN      l10n         l  USING (l10n_id)                -- get default language order
LEFT JOIN user_l10n    ul ON    (ul.l10n_id=l.l10n_id    -- get user preferences if available
                                 AND ul.user_id=$user_id)
WHERE search condition on content, etc
ORDER BY content_id, COALESCE(ul.order,l.order)

您将获得所有匹配的文档,以及用户指定的(或默认的)排序,因此应用程序可以轻松地解决这个问题。

现在的想法是避免从数据库中检索被用户喜欢的语言的现有翻译“遮蔽”的语言的所有行。

执行此操作的自然方法是 GROUP BY,但是 MySQL 没有可以在这里工作的聚合函数...

您可以执行依赖子查询(用于标题和描述);抓取一行还可以,但如果你想抓取多行,速度就非常慢。

但你也可以做点别的事!这依赖于 MySQL 的非标准 GROUP BY 子句的一些可疑行为...

首先,收集要显示的“content_id”列表(分页搜索查询的结果,等等)。然后你就可以做类似下面恐怖的事情:

SELECT * FROM
(
    SELECT content_id, title FROM 
    (
        SELECT    c.content_id, c.title
        FROM      
                  content      c
        JOIN      content_l10n cl USING (content_id)
        JOIN      l10n         l  USING (l10n_id)
        LEFT JOIN user_l10n    ul ON    (ul.l10n_id=l.l10n_id AND ul.user_id=$user_id)
        WHERE cl.content_id IN ($list) AND c.title IS NOT NULL
        ORDER BY content_id, COALESCE(ul.order,l.order)
    ) d GROUP BY content_id
) t
JOIN
(
    SELECT content_id, description FROM 
    (
        SELECT    c.content_id, c.description
        FROM      
                  content      c
        JOIN      content_l10n cl USING (content_id)
        JOIN      l10n         l  USING (l10n_id)
        LEFT JOIN user_l10n    ul ON    (ul.l10n_id=l.l10n_id AND ul.user_id=$user_id)
        WHERE cl.content_id IN ($list) AND c.description IS NOT NULL
        ORDER BY content_id, COALESCE(ul.order,l.order)
    ) d GROUP BY content_id
)
USING (content_id)

This solution works for many rows but you need 1 LEFT JOIN per language, and the order of the JOIns specifies priority.

SELECT   c.url, c.date, 
         COALESCE( c1.title, c2.title ),
         COALESCE( c1.description, c2.description )
FROM      content c
LEFT JOIN content_l10n c1 ON (c1.content_id = c.content_id AND c1.l10n_id=$1)
LEFT JOIN content_l10n c2 ON (c2.content_id = c.content_id AND c2.l10n_id=$2)

(Note : I suppose here that $1 and $2 are the user's first 2 preferred languages, and they are cached in the session, so there is no need for extra JOINs with l10n).

With your table structure this is the only meaningful way to set the language order. You would need an extra table to specify each user's language preferences instead of storing the order in l10n table. So let's suppose you have a table

user_l10n( user_id, l10n_id, order )

And let's suppose the table l10n keeps its "order" field, as a default.

If you do this :

SELECT   ..., COALESCE(ul.order,l.order) AS order
FROM      
          content      c
JOIN      content_l10n cl USING (content_id)
JOIN      l10n         l  USING (l10n_id)                -- get default language order
LEFT JOIN user_l10n    ul ON    (ul.l10n_id=l.l10n_id    -- get user preferences if available
                                 AND ul.user_id=$user_id)
WHERE search condition on content, etc
ORDER BY content_id, COALESCE(ul.order,l.order)

You'll get all the documents that match, and also the user-specified (or default) ordering, so the application can easily sort this out.

Now the idea is to avoid retrieving from the database all the rows that are in languages that are "shadowed" by an existing translation in a language that the user prefers.

The natural way to do this is a GROUP BY, but MySQL doesn't have an aggregate function that would work here...

You could do a dependent subquery (for title and description) ; this would be OK to grab one row, but horribly slow if you want to grab many rows.

But you can also do something else ! That relies on some shady behaviour of MySQL's non-standard GROUP BY clause...

First, gather a list of "content_id"'s that you want to display (result of a paginated search query, whatever). Then you can could do something like the horror that follows :

SELECT * FROM
(
    SELECT content_id, title FROM 
    (
        SELECT    c.content_id, c.title
        FROM      
                  content      c
        JOIN      content_l10n cl USING (content_id)
        JOIN      l10n         l  USING (l10n_id)
        LEFT JOIN user_l10n    ul ON    (ul.l10n_id=l.l10n_id AND ul.user_id=$user_id)
        WHERE cl.content_id IN ($list) AND c.title IS NOT NULL
        ORDER BY content_id, COALESCE(ul.order,l.order)
    ) d GROUP BY content_id
) t
JOIN
(
    SELECT content_id, description FROM 
    (
        SELECT    c.content_id, c.description
        FROM      
                  content      c
        JOIN      content_l10n cl USING (content_id)
        JOIN      l10n         l  USING (l10n_id)
        LEFT JOIN user_l10n    ul ON    (ul.l10n_id=l.l10n_id AND ul.user_id=$user_id)
        WHERE cl.content_id IN ($list) AND c.description IS NOT NULL
        ORDER BY content_id, COALESCE(ul.order,l.order)
    ) d GROUP BY content_id
)
USING (content_id)
难理解 2024-11-12 14:34:11

这应该为您提供第二种情况的基础:

SELECT   content.url, content.date, content_l10n.title, content_l10n.description
FROM     content, content_l10n, l10n
WHERE    content.content_id = content_l10n.content_id AND
         content_l10n.l10n_id = l10n.l10n_id AND
         content.content_id = {$contentId}
ORDER BY l10n.order ASC
LIMIT    1

关于性能,您需要在 content.content_idcontent_l10n.content_idl10n.l10n_id 中建立索引> 和l10n.order

this should give you the basis for the second case:

SELECT   content.url, content.date, content_l10n.title, content_l10n.description
FROM     content, content_l10n, l10n
WHERE    content.content_id = content_l10n.content_id AND
         content_l10n.l10n_id = l10n.l10n_id AND
         content.content_id = {$contentId}
ORDER BY l10n.order ASC
LIMIT    1

About performance, you need an index in content.content_id, content_l10n.content_id, l10n.l10n_id and l10n.order.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文