다국어 틀과 모듈

이 문서에서는 글로벌, 크로스 위키, 다국어 모듈 및 틀을 만드는 방법과 위키미디어의 여러 위키에서 동기화된 상태를 유지하는 방법에 대해 설명합니다.
왜 이것이 필요한가요? 단 하나의 위키백과를 가지고 있지않으며, 300개 이상의 개별 위키백과와 다른 위키프로젝트를 가지고 있습니다. 그리고 누군가가 좋은 새로운 틀이나 Lua 모듈을 만들 때마다 300번 이상 복사되고 번역됩니다. Every translator has to thoroughly understand MediaWiki markup, making copying a very tedious and error-prone process, partially because template authors often assume their templates will be used in just one language. Once copied, the original templates are often improved, and each copy has to be updated while maintaining all existing translations. The pure human expense of copying templates and modules is so high that most of them are either never copied or never updated, especially for the smaller wikis.
이게 최선의 접근 방식입니까? 아니요, 하지만 현재 기술로는 최고의 접근 방식입니다. 시스템 수준에서 이를 가능하게 하려면 미디어위키의 대규모 재작성이 필요합니다. 2001년부터 멀티사이트 틀이 요청되었지만, 단순히 해결하기 어려운 문제이기 때문에 많은 작업이 이루어지지 않았습니다. 이 접근 방식을 사용하면 이제 다국어 콘텐츠를 만들 수 있으며, 미디어위키가 이를 지원하면 많은 노력 없이도 다국어 콘텐츠를 새 시스템으로 쉽게 마이그레이션할 수 있습니다.
예시
Here are some examples of Lua modules that have been globalized and are being kept in sync across wikis using the Synchronizer tool:
- Module:Excerpt (Q52428273-enwiki)
- Module:Transcluder (Q96679044-enwiki)
- Module:Cycling race (Q21707933-wikidatawiki)
- Module:TNT (Q28132212-mediawikiwiki)
일반적인 방법
글로벌 모듈을 만들려면:
- Design or redesign the module so as to provide ways for wikis to localize everything they may need to localize, without having to modify the source code. This page describes several techniques to accomplish this.
- Use the Synchronizer tool to keep the source code identical across Wikimedia wikis.
Best practices
This section describes some of the current best practices to develop global modules.
Naming
This section can be ignored for modules designed to be called from templates only.
Global modules that are meant to be used by other modules should be named the same in all wikis to avoid dependency breaks with other global modules. For example, if a module named A requires a module named B, but in some wiki, module B is named C, then module A will not work in that wiki, unless the source code of module A is changed locally to require C instead of B, which would defeat globalization (of module A).
If a local community does not accept the global name, or renaming is too much trouble, then a workaround is to create a "redirect module" with the global name, that simply requires and returns the module with the local name.
Fortunately, the fact that the Module namespace is named differently in each language doesn't break dependencies, because "Module" is an alias for the Module namespace in all languages.
Master module
Global modules should pick one wiki where to do the development. Generally this will be the home wiki of the module, but it may migrate for various reasons, for example to increase the chances of recruiting new developers by centralizing the development in a larger or more appropriate wiki.
Global modules should start with a comment that includes a link to the master module, to reduce the chances of forking and help recruit new developers (example).
Sandbox
Global modules should have a /sandbox subpage where to test out changes before deploying them to the master module and the other wikis (example).
Testcases
Global modules should have a /testcases subpage with good unit tests to ensure high quality and stability of the module (example). Test cases should:
- Use Module:ScribuntoUnit
- Run with both the main module and the sandbox versions, so that we can compare the results (example)
- Use require('strict') to avoid accidentally using non-declared variables
- Output results both in
/testcases/doc
and the main/doc
page of the module, to catch errors as early as possible
Documentation
Global modules should have a /doc subpage with documentation of all public functions of the module (example) and a section with the testcase runs for both the primary and the sandbox versions of the module (example).
Configuration
Global modules that require configuration should have a separate /config submodule to prevent local wikis from modifying the source code to configure the module (example).
Synchronization
Once a module is able to be copied unchanged to other wikis, the Synchronizer tool can be used to copy it and keep it synced across all Wikimedia wikis.
Backwards compatibility
Global modules should generally keep development backwards-compatible, because changes that are not backwards-compatible will often require manual updates to each and every wiki, template and module that uses the module.
Localization of template parameters
Global modules can have their parameters localized by the template callers. For example, consider the following module that simply outputs the given text (or "Example" if none is given):
local p = {}
function p.main(frame)
local args = frame.args
local text = args['text'] or 'Example'
return text
end
return p
Then a Spanish template would localize the module like so:
{{#invoke:Example|main
| text = {{{texto|Ejemplo}}}
}}
Notice that the template not only localizes the name of the "text" parameter ("texto" means "text" in Spanish), but also the default text ("Ejemplo" means "Example" in Spanish).
See Plantilla:Extracto for a real case of a template that localizes a global module with this technique. Also, see Template:Excerpt for a case where a global module is localized to the English Wikipedia, demonstrating that localization is not always the same as translation.
Localization of user-readable strings
Many modules need to output user-readable strings, such as error messages and interface elements (like buttons). Hard-coding the text of these strings forces other wikis to modify the code in order to localize them, preventing globalization. To avoid this, developers should provide ways to localize user-readable strings without having to modify the code itself. This section explains several ways to accomplish this.
Template parameters
User-readable strings can be localized through template parameters when calling the module. This approach is convenient when:
- The text is likely to vary with each template call
- The text is likely to be changed by users when calling the template
- The text is likely to contain a magic word, a template call, a parser function or some other wiki element
An example of a module using this approach would be:
local p = {}
function p.main(frame)
local args = frame.args
local text = args['text'] or 'Example'
return text
end
return p
This way, every template may modify the text when calling the module, like so:
{{#invoke:Example|main
| text = Ejemplo
}}
Notice that in this example, if a template calls the module without specifying the text
parameter, then the hard-coded English text 'Example' would be used.
This is not necessary.
Modules may require template callers to set the text
parameter by throwing an error if they don't.
However, it's often friendlier to have a fallback.
Config file
Another way to localize user-readable strings is through a separate /config subpage. This approach is convenient when:
- The module is meant to be called by many templates per wiki, thus allowing localization to be done only once and then reused
- There're many messages to localize, so it's easier to have them all together in their own place
- There's already a need for a /config file for other reasons, so we might as well use it for localization too
An example of a module using this approach would be:
local config = require('Module:Example/config')
local p = {}
function p.main(frame)
local text = config.text or 'Example'
return text
end
return p
Then wikis would be able to create /config files like the following:
return {
text = 'Ejemplo'
}
Translation tables
Another way to localize user-readable strings is through a central translation table at Commons. This approach is convenient when:
- The strings should vary with the preferred language of the user, rather than the language of the wiki or page.
- We want to centralize localization efforts on a single page.
The Module:TNT was created specifically to get strings from translation tables. An example module using TNT could look like this:
local TNT = require('Module:TNT')
local p = {}
function p.main(frame)
local text = TNT.format('I18n/Example', 'text')
return text
end
return p
See Data:I18n/Template:Graphs.tab for a simple but real example of a translation table with two messages, each having a single parameter. It's important to store parameters as parts of the strings because in many languages the parameter would have to be placed at a different position in the string according to the norms of the language.
Translation tables should start with the "Data:I18n/..." prefix to separate them from other types of tabular data. If a message has not yet been localized, TNT will fall back to English (or other fallback language as defined by the language's fallback sequence ). TNT also supports all standard localization conventions such as {{PLURAL|...}} and other parameters .
One downside of this approach is that it requires installing and setting up Extension:JsonConfig , which may not have been done on non-Wikimedia wikis, limiting the ability to reuse these modules on third-party wikis.
MediaWiki messages
In some cases, MediaWiki itself (or some extension) may have the messages we need already localized. For example, if we need the string "New page" we may use MediaWiki:Newpage, like so:
local p = {}
function p.main(frame)
local msg = mw.message.new('newpage')
local text = msg:plain()
return text
end
return p
See Special:AllMessages for a list of all available messages.
All of the above
Depending on the case, all of the above methods may be combined. For example, MediaWiki messages may be used when available, and when not, a translation table or config file is queried, and if no localization is found there, then a hard-coded English text is used, unless a template parameter overrides it.
Combining several methods can be effective, but the benefits should be weighted against the downsides of the increased complexity, which may cause performance loss and bugs, as well as more difficulty in maintaining the code and recruiting new developers.
Template data
Template parameters are usually stored as a JSON templatedata block inside the template's /doc
subpage.
This makes it convenient to translate, but when a new parameter is added to a global template, all /doc
pages need to be updated in every language.
Module:TNT helps with this by automatically generating the templatedata block from a table stored on Commons.
Placing the following line into every /doc
subpage will use Data:Templatedata/Graph:Lines.tab table to generate all the needed templatedata information in every language.
Even if the local community has not translated the full template documentation, they will be able to see all template parameters, centrally updated.
{{#invoke:TNT|doc|Graph:Lines}}
See also
- Wishlist proposal 2019 (40 votes)
- T122086 - RFC: Sharing templates and modules between wikis - poor man's version (original idea for this bot)
- T121470 - Central Global Repository for Templates, Lua modules, and Gadgets ticket (main ticket for everything cross-site shareable)
- T41610 - Scribunto should support global module invocations
- Global templates/Proposed specification, short version - Proposal to implement a similar idea comprehensively, without the need for the special tools, and with full support in MediaWiki core and extensions
- Global gadgets - Gadgets that are designed and ready to be used in any wiki