From 567316cfbeb718811b8319b05e2bcffe7efd1fcb Mon Sep 17 00:00:00 2001 From: infiinte-persistence Date: Fri, 9 Oct 2020 13:38:03 +0800 Subject: [PATCH] Support for multiple string context + "About" as initial example. ## Issue 4796 - i18n: Allow support for string overloading (multiple contexts) ## Approach - Minimal code and process change. - Handle on a case-by-case basis when reported by translators. - Split the affected key in the string json by appending the context. - Translators need to be aware of the new format and not translate context itself. Code is added to detect bad translations and will revert to English. Sample in json: "About --[About section in Help Page]--": "About", "About --[tab title in Channel Page]--": "About", Sample in client code: title={__('About --[About section in Help Page]--')} - "--[ ]--" was chosen as it's unique enough (unlikely for real strings to use it) and hopefully not that distracting in the client code. - In the key itself, spaces are allowed after the string (i.e. before '--[') for neatness. It will be trimmed by the system. ## First example "About" is used in 3 places: - Channel Page - Help Page - Footer (in Odysee branch) For Russian, the word "About" is "O" and is usually not used standalone, but requires something behind it. A translator said so, and seems to be the case in other sites as well. "O xxx" "O yyy" ## Other languages For other languages that are not impacted, they can just clone the same translation for each of the split keys, just like in English. ## Possible enhancement in Transifex I see that Transifex's API includes a `context` entry. It might be possible to move the context-metadata there during the upload, so translators will never see the "--[]--" messiness (it will be shown as "Context: xxx" in the Transifex GUI). I'm not sure how to test the Transifex side, so I did not investigate further. --- static/app-strings.json | 3 ++- ui/i18n.js | 34 +++++++++++++++++++++++++++++----- ui/page/channel/view.jsx | 2 +- ui/page/help/view.jsx | 2 +- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/static/app-strings.json b/static/app-strings.json index cc9520136..9a034a598 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -88,7 +88,8 @@ "Find New Channels": "Find New Channels", "Discover New Channels": "Discover New Channels", "publishes": "publishes", - "About": "About", + "About --[About section in Help Page]--": "About", + "About --[tab title in Channel Page]--": "About", "Share Channel": "Share Channel", "Go to page:": "Go to page:", "Enter a URL for your thumbnail.": "Enter a URL for your thumbnail.", diff --git a/ui/i18n.js b/ui/i18n.js index 470362649..9faa2cc50 100644 --- a/ui/i18n.js +++ b/ui/i18n.js @@ -21,14 +21,14 @@ function saveMessage(message) { try { knownMessages = JSON.parse(fs.readFileSync(messagesFilePath, 'utf-8')); } catch (err) { - throw 'Error parsing i18n messages file: ' + messagesFilePath + ' err: ' + err; + throw new Error('Error parsing i18n messages file: ' + messagesFilePath + ' err: ' + err); } } if (!knownMessages[message]) { const END = '--end--'; delete knownMessages[END]; - knownMessages[message] = message; + knownMessages[message] = removeContextMetadata(message); knownMessages[END] = END; fs.writeFile(messagesFilePath, JSON.stringify(knownMessages, null, 2) + '\n', 'utf-8', err => { @@ -53,6 +53,31 @@ function saveMessage(message) { } // @endif +function removeContextMetadata(message) { + // Example string entries with context-metadata: + // "About --[About section in Help Page]--": "About", + // "About --[tab title in Channel Page]--": "About", + const CONTEXT_BEGIN = '--['; + const CONTEXT_FINAL = ']--'; + + // If the resolved string still contains the context-metadata, then it's one of the following: + // 1. In development mode, where 'en.json' in the server hasn't been updated with the string yet. + // 2. Translator made a mistake of not ignoring the context string. + // In either case, we'll revert to the English version. + + const begin = message.lastIndexOf(CONTEXT_BEGIN); + if (begin > 0 && message.endsWith(CONTEXT_FINAL)) { + // Strip away context: + message = message.substring(0, begin); + // No trailing spaces should be allowed in the string database anyway, because that is hard to translate + // (can't see in Transifex; might not make sense in other languages; etc.). + // With that, we can add a space before the context-metadata to make it neat, and trim both cases here: + message = message.trimEnd(); + } + + return message; +} + export function __(message, tokens) { const language = localStorageAvailable ? window.localStorage.getItem('language') || 'en' @@ -62,9 +87,8 @@ export function __(message, tokens) { saveMessage(message); } - const translatedMessage = window.i18n_messages[language] - ? window.i18n_messages[language][message] || message - : message; + let translatedMessage = window.i18n_messages[language] ? window.i18n_messages[language][message] || message : message; + translatedMessage = removeContextMetadata(translatedMessage); if (!tokens) { return translatedMessage; diff --git a/ui/page/channel/view.jsx b/ui/page/channel/view.jsx index e74178bc4..a84c0cb13 100644 --- a/ui/page/channel/view.jsx +++ b/ui/page/channel/view.jsx @@ -200,7 +200,7 @@ function ChannelPage(props: Props) { {__('Content')} - {editing ? __('Editing Your Channel') : __('About')} + {editing ? __('Editing Your Channel') : __('About --[tab title in Channel Page]--')} {__('Discussion')} diff --git a/ui/page/help/view.jsx b/ui/page/help/view.jsx index 9fe6a601a..b0d9d4c3e 100644 --- a/ui/page/help/view.jsx +++ b/ui/page/help/view.jsx @@ -212,7 +212,7 @@ class HelpPage extends React.PureComponent { {/* @endif */}